Linux Audio

Check our new training course

Linux BSP upgrade and security maintenance

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