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 2854dcadd5SBen Skeggs void 292ca0ddbcSBen Skeggs nvkm_mc_unk260(struct nvkm_mc *mc, u32 data) 30c39f472eSBen Skeggs { 3154dcadd5SBen Skeggs if (mc->func->unk260) 3254dcadd5SBen Skeggs mc->func->unk260(mc, data); 33c39f472eSBen Skeggs } 34c39f472eSBen Skeggs 35c39f472eSBen Skeggs static inline u32 362ca0ddbcSBen Skeggs nvkm_mc_intr_mask(struct nvkm_mc *mc) 37c39f472eSBen Skeggs { 3825e3a463SBen Skeggs struct nvkm_device *device = mc->subdev.device; 3925e3a463SBen Skeggs u32 intr = nvkm_rd32(device, 0x000100); 40c39f472eSBen Skeggs if (intr == 0xffffffff) /* likely fallen off the bus */ 41c39f472eSBen Skeggs intr = 0x00000000; 42c39f472eSBen Skeggs return intr; 43c39f472eSBen Skeggs } 44c39f472eSBen Skeggs 45c39f472eSBen Skeggs static irqreturn_t 46d7e5fcd2SBen Skeggs nvkm_mc_intr(int irq, void *arg) 47c39f472eSBen Skeggs { 482ca0ddbcSBen Skeggs struct nvkm_mc *mc = arg; 49c47a48a5SBen Skeggs struct nvkm_subdev *subdev = &mc->subdev; 50c47a48a5SBen Skeggs struct nvkm_device *device = subdev->device; 5154dcadd5SBen Skeggs const struct nvkm_mc_intr *map = mc->func->intr; 52d7e5fcd2SBen Skeggs struct nvkm_subdev *unit; 53c39f472eSBen Skeggs u32 intr; 54c39f472eSBen Skeggs 5525e3a463SBen Skeggs nvkm_wr32(device, 0x000140, 0x00000000); 5625e3a463SBen Skeggs nvkm_rd32(device, 0x000140); 572ca0ddbcSBen Skeggs intr = nvkm_mc_intr_mask(mc); 582ca0ddbcSBen Skeggs if (mc->use_msi) 5954dcadd5SBen Skeggs mc->func->msi_rearm(mc); 60c39f472eSBen Skeggs 61c39f472eSBen Skeggs if (intr) { 622ca0ddbcSBen Skeggs u32 stat = intr = nvkm_mc_intr_mask(mc); 63c39f472eSBen Skeggs while (map->stat) { 64c39f472eSBen Skeggs if (intr & map->stat) { 6554dcadd5SBen Skeggs unit = nvkm_device_subdev(device, map->unit); 666cf813fbSBen Skeggs if (unit) 676cf813fbSBen Skeggs nvkm_subdev_intr(unit); 68c39f472eSBen Skeggs stat &= ~map->stat; 69c39f472eSBen Skeggs } 70c39f472eSBen Skeggs map++; 71c39f472eSBen Skeggs } 72c39f472eSBen Skeggs 73c39f472eSBen Skeggs if (stat) 74c47a48a5SBen Skeggs nvkm_error(subdev, "unknown intr %08x\n", stat); 75c39f472eSBen Skeggs } 76c39f472eSBen Skeggs 7725e3a463SBen Skeggs nvkm_wr32(device, 0x000140, 0x00000001); 78c39f472eSBen Skeggs return intr ? IRQ_HANDLED : IRQ_NONE; 79c39f472eSBen Skeggs } 80c39f472eSBen Skeggs 8154dcadd5SBen Skeggs static int 8254dcadd5SBen Skeggs nvkm_mc_fini(struct nvkm_subdev *subdev, bool suspend) 83c39f472eSBen Skeggs { 8454dcadd5SBen Skeggs nvkm_wr32(subdev->device, 0x000140, 0x00000000); 8554dcadd5SBen Skeggs return 0; 86c39f472eSBen Skeggs } 87c39f472eSBen Skeggs 8854dcadd5SBen Skeggs static int 8954dcadd5SBen Skeggs nvkm_mc_oneinit(struct nvkm_subdev *subdev) 90c39f472eSBen Skeggs { 9154dcadd5SBen Skeggs struct nvkm_mc *mc = nvkm_mc(subdev); 9254dcadd5SBen Skeggs return request_irq(mc->irq, nvkm_mc_intr, IRQF_SHARED, "nvkm", mc); 9354dcadd5SBen Skeggs } 9454dcadd5SBen Skeggs 9554dcadd5SBen Skeggs static int 9654dcadd5SBen Skeggs nvkm_mc_init(struct nvkm_subdev *subdev) 9754dcadd5SBen Skeggs { 9854dcadd5SBen Skeggs struct nvkm_mc *mc = nvkm_mc(subdev); 9925e3a463SBen Skeggs struct nvkm_device *device = mc->subdev.device; 10054dcadd5SBen Skeggs if (mc->func->init) 10154dcadd5SBen Skeggs mc->func->init(mc); 10225e3a463SBen Skeggs nvkm_wr32(device, 0x000140, 0x00000001); 103c39f472eSBen Skeggs return 0; 104c39f472eSBen Skeggs } 105c39f472eSBen Skeggs 10654dcadd5SBen Skeggs static void * 10754dcadd5SBen Skeggs nvkm_mc_dtor(struct nvkm_subdev *subdev) 108c39f472eSBen Skeggs { 10954dcadd5SBen Skeggs struct nvkm_mc *mc = nvkm_mc(subdev); 11025e3a463SBen Skeggs struct nvkm_device *device = mc->subdev.device; 1112ca0ddbcSBen Skeggs free_irq(mc->irq, mc); 1122ca0ddbcSBen Skeggs if (mc->use_msi) 113c39f472eSBen Skeggs pci_disable_msi(device->pdev); 11454dcadd5SBen Skeggs return mc; 115c39f472eSBen Skeggs } 116c39f472eSBen Skeggs 11754dcadd5SBen Skeggs static const struct nvkm_subdev_func 11854dcadd5SBen Skeggs nvkm_mc = { 11954dcadd5SBen Skeggs .dtor = nvkm_mc_dtor, 12054dcadd5SBen Skeggs .oneinit = nvkm_mc_oneinit, 12154dcadd5SBen Skeggs .init = nvkm_mc_init, 12254dcadd5SBen Skeggs .fini = nvkm_mc_fini, 12354dcadd5SBen Skeggs }; 12454dcadd5SBen Skeggs 125c39f472eSBen Skeggs int 12654dcadd5SBen Skeggs nvkm_mc_new_(const struct nvkm_mc_func *func, struct nvkm_device *device, 12754dcadd5SBen Skeggs int index, struct nvkm_mc **pmc) 128c39f472eSBen Skeggs { 1292ca0ddbcSBen Skeggs struct nvkm_mc *mc; 130c39f472eSBen Skeggs int ret; 131c39f472eSBen Skeggs 13254dcadd5SBen Skeggs if (!(mc = *pmc = kzalloc(sizeof(*mc), GFP_KERNEL))) 13354dcadd5SBen Skeggs return -ENOMEM; 134c39f472eSBen Skeggs 13554dcadd5SBen Skeggs nvkm_subdev_ctor(&nvkm_mc, device, index, 0, &mc->subdev); 13654dcadd5SBen Skeggs mc->func = func; 137c39f472eSBen Skeggs 138c39f472eSBen Skeggs if (nv_device_is_pci(device)) { 139c39f472eSBen Skeggs switch (device->pdev->device & 0x0ff0) { 140c39f472eSBen Skeggs case 0x00f0: 141c39f472eSBen Skeggs case 0x02e0: 142c39f472eSBen Skeggs /* BR02? NFI how these would be handled yet exactly */ 143c39f472eSBen Skeggs break; 144c39f472eSBen Skeggs default: 145c39f472eSBen Skeggs switch (device->chipset) { 146c39f472eSBen Skeggs case 0xaa: 147c39f472eSBen Skeggs /* reported broken, nv also disable it */ 148c39f472eSBen Skeggs break; 149c39f472eSBen Skeggs default: 1502ca0ddbcSBen Skeggs mc->use_msi = true; 151c39f472eSBen Skeggs break; 152c39f472eSBen Skeggs } 153c39f472eSBen Skeggs } 154c39f472eSBen Skeggs 1552ca0ddbcSBen Skeggs mc->use_msi = nvkm_boolopt(device->cfgopt, "NvMSI", 1562ca0ddbcSBen Skeggs mc->use_msi); 157c39f472eSBen Skeggs 15854dcadd5SBen Skeggs if (mc->use_msi && mc->func->msi_rearm) { 1592ca0ddbcSBen Skeggs mc->use_msi = pci_enable_msi(device->pdev) == 0; 1602ca0ddbcSBen Skeggs if (mc->use_msi) { 161c47a48a5SBen Skeggs nvkm_debug(&mc->subdev, "MSI enabled\n"); 16254dcadd5SBen Skeggs mc->func->msi_rearm(mc); 163c39f472eSBen Skeggs } 164c39f472eSBen Skeggs } else { 1652ca0ddbcSBen Skeggs mc->use_msi = false; 166c39f472eSBen Skeggs } 167c39f472eSBen Skeggs } 168c39f472eSBen Skeggs 169c39f472eSBen Skeggs ret = nv_device_get_irq(device, true); 170c39f472eSBen Skeggs if (ret < 0) 171c39f472eSBen Skeggs return ret; 1722ca0ddbcSBen Skeggs mc->irq = ret; 173c39f472eSBen Skeggs return 0; 174c39f472eSBen Skeggs } 175