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> 27c39f472eSBen Skeggs 28c39f472eSBen Skeggs static inline void 292ca0ddbcSBen Skeggs nvkm_mc_unk260(struct nvkm_mc *mc, u32 data) 30c39f472eSBen Skeggs { 312ca0ddbcSBen Skeggs const struct nvkm_mc_oclass *impl = (void *)nv_oclass(mc); 32c39f472eSBen Skeggs if (impl->unk260) 332ca0ddbcSBen Skeggs impl->unk260(mc, data); 34c39f472eSBen Skeggs } 35c39f472eSBen Skeggs 36c39f472eSBen Skeggs static inline u32 372ca0ddbcSBen Skeggs nvkm_mc_intr_mask(struct nvkm_mc *mc) 38c39f472eSBen Skeggs { 3925e3a463SBen Skeggs struct nvkm_device *device = mc->subdev.device; 4025e3a463SBen Skeggs u32 intr = nvkm_rd32(device, 0x000100); 41c39f472eSBen Skeggs if (intr == 0xffffffff) /* likely fallen off the bus */ 42c39f472eSBen Skeggs intr = 0x00000000; 43c39f472eSBen Skeggs return intr; 44c39f472eSBen Skeggs } 45c39f472eSBen Skeggs 46c39f472eSBen Skeggs static irqreturn_t 47d7e5fcd2SBen Skeggs nvkm_mc_intr(int irq, void *arg) 48c39f472eSBen Skeggs { 492ca0ddbcSBen Skeggs struct nvkm_mc *mc = arg; 5025e3a463SBen Skeggs struct nvkm_device *device = mc->subdev.device; 512ca0ddbcSBen Skeggs const struct nvkm_mc_oclass *oclass = (void *)nv_object(mc)->oclass; 52d7e5fcd2SBen Skeggs const struct nvkm_mc_intr *map = oclass->intr; 53d7e5fcd2SBen Skeggs struct nvkm_subdev *unit; 54c39f472eSBen Skeggs u32 intr; 55c39f472eSBen Skeggs 5625e3a463SBen Skeggs nvkm_wr32(device, 0x000140, 0x00000000); 5725e3a463SBen Skeggs nvkm_rd32(device, 0x000140); 582ca0ddbcSBen Skeggs intr = nvkm_mc_intr_mask(mc); 592ca0ddbcSBen Skeggs if (mc->use_msi) 602ca0ddbcSBen Skeggs oclass->msi_rearm(mc); 61c39f472eSBen Skeggs 62c39f472eSBen Skeggs if (intr) { 632ca0ddbcSBen Skeggs u32 stat = intr = nvkm_mc_intr_mask(mc); 64c39f472eSBen Skeggs while (map->stat) { 65c39f472eSBen Skeggs if (intr & map->stat) { 662ca0ddbcSBen Skeggs unit = nvkm_subdev(mc, map->unit); 67c39f472eSBen Skeggs if (unit && unit->intr) 68c39f472eSBen Skeggs unit->intr(unit); 69c39f472eSBen Skeggs stat &= ~map->stat; 70c39f472eSBen Skeggs } 71c39f472eSBen Skeggs map++; 72c39f472eSBen Skeggs } 73c39f472eSBen Skeggs 74c39f472eSBen Skeggs if (stat) 752ca0ddbcSBen Skeggs nv_error(mc, "unknown intr 0x%08x\n", stat); 76c39f472eSBen Skeggs } 77c39f472eSBen Skeggs 7825e3a463SBen Skeggs nvkm_wr32(device, 0x000140, 0x00000001); 79c39f472eSBen Skeggs return intr ? IRQ_HANDLED : IRQ_NONE; 80c39f472eSBen Skeggs } 81c39f472eSBen Skeggs 82c39f472eSBen Skeggs int 83d7e5fcd2SBen Skeggs _nvkm_mc_fini(struct nvkm_object *object, bool suspend) 84c39f472eSBen Skeggs { 852ca0ddbcSBen Skeggs struct nvkm_mc *mc = (void *)object; 8625e3a463SBen Skeggs struct nvkm_device *device = mc->subdev.device; 8725e3a463SBen Skeggs nvkm_wr32(device, 0x000140, 0x00000000); 882ca0ddbcSBen Skeggs return nvkm_subdev_fini(&mc->subdev, suspend); 89c39f472eSBen Skeggs } 90c39f472eSBen Skeggs 91c39f472eSBen Skeggs int 92d7e5fcd2SBen Skeggs _nvkm_mc_init(struct nvkm_object *object) 93c39f472eSBen Skeggs { 942ca0ddbcSBen Skeggs struct nvkm_mc *mc = (void *)object; 9525e3a463SBen Skeggs struct nvkm_device *device = mc->subdev.device; 962ca0ddbcSBen Skeggs int ret = nvkm_subdev_init(&mc->subdev); 97c39f472eSBen Skeggs if (ret) 98c39f472eSBen Skeggs return ret; 9925e3a463SBen Skeggs nvkm_wr32(device, 0x000140, 0x00000001); 100c39f472eSBen Skeggs return 0; 101c39f472eSBen Skeggs } 102c39f472eSBen Skeggs 103c39f472eSBen Skeggs void 104d7e5fcd2SBen Skeggs _nvkm_mc_dtor(struct nvkm_object *object) 105c39f472eSBen Skeggs { 1062ca0ddbcSBen Skeggs struct nvkm_mc *mc = (void *)object; 10725e3a463SBen Skeggs struct nvkm_device *device = mc->subdev.device; 1082ca0ddbcSBen Skeggs free_irq(mc->irq, mc); 1092ca0ddbcSBen Skeggs if (mc->use_msi) 110c39f472eSBen Skeggs pci_disable_msi(device->pdev); 1112ca0ddbcSBen Skeggs nvkm_subdev_destroy(&mc->subdev); 112c39f472eSBen Skeggs } 113c39f472eSBen Skeggs 114c39f472eSBen Skeggs int 115d7e5fcd2SBen Skeggs nvkm_mc_create_(struct nvkm_object *parent, struct nvkm_object *engine, 116d7e5fcd2SBen Skeggs struct nvkm_oclass *bclass, int length, void **pobject) 117c39f472eSBen Skeggs { 118d7e5fcd2SBen Skeggs const struct nvkm_mc_oclass *oclass = (void *)bclass; 11925e3a463SBen Skeggs struct nvkm_device *device = (void *)parent; 1202ca0ddbcSBen Skeggs struct nvkm_mc *mc; 121c39f472eSBen Skeggs int ret; 122c39f472eSBen Skeggs 123d7e5fcd2SBen Skeggs ret = nvkm_subdev_create_(parent, engine, bclass, 0, "PMC", 124c39f472eSBen Skeggs "master", length, pobject); 1252ca0ddbcSBen Skeggs mc = *pobject; 126c39f472eSBen Skeggs if (ret) 127c39f472eSBen Skeggs return ret; 128c39f472eSBen Skeggs 1292ca0ddbcSBen Skeggs mc->unk260 = nvkm_mc_unk260; 130c39f472eSBen Skeggs 131c39f472eSBen Skeggs if (nv_device_is_pci(device)) { 132c39f472eSBen Skeggs switch (device->pdev->device & 0x0ff0) { 133c39f472eSBen Skeggs case 0x00f0: 134c39f472eSBen Skeggs case 0x02e0: 135c39f472eSBen Skeggs /* BR02? NFI how these would be handled yet exactly */ 136c39f472eSBen Skeggs break; 137c39f472eSBen Skeggs default: 138c39f472eSBen Skeggs switch (device->chipset) { 139c39f472eSBen Skeggs case 0xaa: 140c39f472eSBen Skeggs /* reported broken, nv also disable it */ 141c39f472eSBen Skeggs break; 142c39f472eSBen Skeggs default: 1432ca0ddbcSBen Skeggs mc->use_msi = true; 144c39f472eSBen Skeggs break; 145c39f472eSBen Skeggs } 146c39f472eSBen Skeggs } 147c39f472eSBen Skeggs 1482ca0ddbcSBen Skeggs mc->use_msi = nvkm_boolopt(device->cfgopt, "NvMSI", 1492ca0ddbcSBen Skeggs mc->use_msi); 150c39f472eSBen Skeggs 1512ca0ddbcSBen Skeggs if (mc->use_msi && oclass->msi_rearm) { 1522ca0ddbcSBen Skeggs mc->use_msi = pci_enable_msi(device->pdev) == 0; 1532ca0ddbcSBen Skeggs if (mc->use_msi) { 1542ca0ddbcSBen Skeggs nv_info(mc, "MSI interrupts enabled\n"); 1552ca0ddbcSBen Skeggs oclass->msi_rearm(mc); 156c39f472eSBen Skeggs } 157c39f472eSBen Skeggs } else { 1582ca0ddbcSBen Skeggs mc->use_msi = false; 159c39f472eSBen Skeggs } 160c39f472eSBen Skeggs } 161c39f472eSBen Skeggs 162c39f472eSBen Skeggs ret = nv_device_get_irq(device, true); 163c39f472eSBen Skeggs if (ret < 0) 164c39f472eSBen Skeggs return ret; 1652ca0ddbcSBen Skeggs mc->irq = ret; 166c39f472eSBen Skeggs 1672ca0ddbcSBen Skeggs ret = request_irq(mc->irq, nvkm_mc_intr, IRQF_SHARED, "nvkm", mc); 168c39f472eSBen Skeggs if (ret < 0) 169c39f472eSBen Skeggs return ret; 170c39f472eSBen Skeggs 171c39f472eSBen Skeggs return 0; 172c39f472eSBen Skeggs } 173