Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,7 @@
# SPDX-License-Identifier: MIT
nvkm-y += nvkm/engine/device/acpi.o
nvkm-y += nvkm/engine/device/base.o
nvkm-y += nvkm/engine/device/ctrl.o
nvkm-y += nvkm/engine/device/pci.o
nvkm-y += nvkm/engine/device/tegra.o
nvkm-y += nvkm/engine/device/user.o
@@ -0,0 +1,58 @@
/*
* Copyright 2014 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include "acpi.h"
#include <core/device.h>
#include <subdev/clk.h>
#ifdef CONFIG_ACPI
static int
nvkm_acpi_ntfy(struct notifier_block *nb, unsigned long val, void *data)
{
struct nvkm_device *device = container_of(nb, typeof(*device), acpi.nb);
struct acpi_bus_event *info = data;
if (!strcmp(info->device_class, "ac_adapter"))
nvkm_clk_pwrsrc(device);
return NOTIFY_DONE;
}
#endif
void
nvkm_acpi_fini(struct nvkm_device *device)
{
#ifdef CONFIG_ACPI
unregister_acpi_notifier(&device->acpi.nb);
#endif
}
void
nvkm_acpi_init(struct nvkm_device *device)
{
#ifdef CONFIG_ACPI
device->acpi.nb.notifier_call = nvkm_acpi_ntfy;
register_acpi_notifier(&device->acpi.nb);
#endif
}
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVKM_DEVICE_ACPI_H__
#define __NVKM_DEVICE_ACPI_H__
#include <core/os.h>
struct nvkm_device;
void nvkm_acpi_init(struct nvkm_device *);
void nvkm_acpi_fini(struct nvkm_device *);
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,212 @@
/*
* Copyright 2013 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs <bskeggs@redhat.com>
*/
#include "ctrl.h"
#include <core/client.h>
#include <subdev/clk.h>
#include <nvif/class.h>
#include <nvif/if0001.h>
#include <nvif/ioctl.h>
#include <nvif/unpack.h>
static int
nvkm_control_mthd_pstate_info(struct nvkm_control *ctrl, void *data, u32 size)
{
union {
struct nvif_control_pstate_info_v0 v0;
} *args = data;
struct nvkm_clk *clk = ctrl->device->clk;
int ret = -ENOSYS;
nvif_ioctl(&ctrl->object, "control pstate info size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(&ctrl->object, "control pstate info vers %d\n",
args->v0.version);
} else
return ret;
if (clk) {
args->v0.count = clk->state_nr;
args->v0.ustate_ac = clk->ustate_ac;
args->v0.ustate_dc = clk->ustate_dc;
args->v0.pwrsrc = clk->pwrsrc;
args->v0.pstate = clk->pstate;
} else {
args->v0.count = 0;
args->v0.ustate_ac = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE;
args->v0.ustate_dc = NVIF_CONTROL_PSTATE_INFO_V0_USTATE_DISABLE;
args->v0.pwrsrc = -ENODEV;
args->v0.pstate = NVIF_CONTROL_PSTATE_INFO_V0_PSTATE_UNKNOWN;
}
return 0;
}
static int
nvkm_control_mthd_pstate_attr(struct nvkm_control *ctrl, void *data, u32 size)
{
union {
struct nvif_control_pstate_attr_v0 v0;
} *args = data;
struct nvkm_clk *clk = ctrl->device->clk;
const struct nvkm_domain *domain;
struct nvkm_pstate *pstate;
struct nvkm_cstate *cstate;
int i = 0, j = -1;
u32 lo, hi;
int ret = -ENOSYS;
nvif_ioctl(&ctrl->object, "control pstate attr size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(&ctrl->object,
"control pstate attr vers %d state %d index %d\n",
args->v0.version, args->v0.state, args->v0.index);
if (!clk)
return -ENODEV;
if (args->v0.state < NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT)
return -EINVAL;
if (args->v0.state >= clk->state_nr)
return -EINVAL;
} else
return ret;
domain = clk->domains;
while (domain->name != nv_clk_src_max) {
if (domain->mname && ++j == args->v0.index)
break;
domain++;
}
if (domain->name == nv_clk_src_max)
return -EINVAL;
if (args->v0.state != NVIF_CONTROL_PSTATE_ATTR_V0_STATE_CURRENT) {
list_for_each_entry(pstate, &clk->states, head) {
if (i++ == args->v0.state)
break;
}
lo = pstate->base.domain[domain->name];
hi = lo;
list_for_each_entry(cstate, &pstate->list, head) {
lo = min(lo, cstate->domain[domain->name]);
hi = max(hi, cstate->domain[domain->name]);
}
args->v0.state = pstate->pstate;
} else {
lo = max(nvkm_clk_read(clk, domain->name), 0);
hi = lo;
}
snprintf(args->v0.name, sizeof(args->v0.name), "%s", domain->mname);
snprintf(args->v0.unit, sizeof(args->v0.unit), "MHz");
args->v0.min = lo / domain->mdiv;
args->v0.max = hi / domain->mdiv;
args->v0.index = 0;
while ((++domain)->name != nv_clk_src_max) {
if (domain->mname) {
args->v0.index = ++j;
break;
}
}
return 0;
}
static int
nvkm_control_mthd_pstate_user(struct nvkm_control *ctrl, void *data, u32 size)
{
union {
struct nvif_control_pstate_user_v0 v0;
} *args = data;
struct nvkm_clk *clk = ctrl->device->clk;
int ret = -ENOSYS;
nvif_ioctl(&ctrl->object, "control pstate user size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(&ctrl->object,
"control pstate user vers %d ustate %d pwrsrc %d\n",
args->v0.version, args->v0.ustate, args->v0.pwrsrc);
if (!clk)
return -ENODEV;
} else
return ret;
if (args->v0.pwrsrc >= 0) {
ret |= nvkm_clk_ustate(clk, args->v0.ustate, args->v0.pwrsrc);
} else {
ret |= nvkm_clk_ustate(clk, args->v0.ustate, 0);
ret |= nvkm_clk_ustate(clk, args->v0.ustate, 1);
}
return ret;
}
static int
nvkm_control_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
{
struct nvkm_control *ctrl = nvkm_control(object);
switch (mthd) {
case NVIF_CONTROL_PSTATE_INFO:
return nvkm_control_mthd_pstate_info(ctrl, data, size);
case NVIF_CONTROL_PSTATE_ATTR:
return nvkm_control_mthd_pstate_attr(ctrl, data, size);
case NVIF_CONTROL_PSTATE_USER:
return nvkm_control_mthd_pstate_user(ctrl, data, size);
default:
break;
}
return -EINVAL;
}
static const struct nvkm_object_func
nvkm_control = {
.mthd = nvkm_control_mthd,
};
static int
nvkm_control_new(struct nvkm_device *device, const struct nvkm_oclass *oclass,
void *data, u32 size, struct nvkm_object **pobject)
{
struct nvkm_control *ctrl;
if (!(ctrl = kzalloc_obj(*ctrl)))
return -ENOMEM;
*pobject = &ctrl->object;
ctrl->device = device;
nvkm_object_ctor(&nvkm_control, oclass, &ctrl->object);
return 0;
}
const struct nvkm_device_oclass
nvkm_control_oclass = {
.base.oclass = NVIF_CLASS_CONTROL,
.base.minver = -1,
.base.maxver = -1,
.ctor = nvkm_control_new,
};
@@ -0,0 +1,13 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVKM_DEVICE_CTRL_H__
#define __NVKM_DEVICE_CTRL_H__
#define nvkm_control(p) container_of((p), struct nvkm_control, object)
#include <core/object.h>
struct nvkm_control {
struct nvkm_object object;
struct nvkm_device *device;
};
extern const struct nvkm_device_oclass nvkm_control_oclass;
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: MIT */
#ifndef __NVKM_DEVICE_PRIV_H__
#define __NVKM_DEVICE_PRIV_H__
#include <core/device.h>
#include <subdev/acr.h>
#include <subdev/bar.h>
#include <subdev/bios.h>
#include <subdev/bus.h>
#include <subdev/clk.h>
#include <subdev/devinit.h>
#include <subdev/fault.h>
#include <subdev/fb.h>
#include <subdev/fsp.h>
#include <subdev/fuse.h>
#include <subdev/gpio.h>
#include <subdev/gsp.h>
#include <subdev/i2c.h>
#include <subdev/iccsense.h>
#include <subdev/instmem.h>
#include <subdev/ltc.h>
#include <subdev/mc.h>
#include <subdev/mmu.h>
#include <subdev/mxm.h>
#include <subdev/pci.h>
#include <subdev/pmu.h>
#include <subdev/privring.h>
#include <subdev/therm.h>
#include <subdev/timer.h>
#include <subdev/top.h>
#include <subdev/vfn.h>
#include <subdev/volt.h>
#include <engine/bsp.h>
#include <engine/ce.h>
#include <engine/cipher.h>
#include <engine/disp.h>
#include <engine/dma.h>
#include <engine/fifo.h>
#include <engine/gr.h>
#include <engine/mpeg.h>
#include <engine/mspdec.h>
#include <engine/msppp.h>
#include <engine/msvld.h>
#include <engine/nvenc.h>
#include <engine/nvdec.h>
#include <engine/sec.h>
#include <engine/sec2.h>
#include <engine/sw.h>
#include <engine/vic.h>
#include <engine/vp.h>
int nvkm_device_ctor(const struct nvkm_device_func *,
const struct nvkm_device_quirk *,
struct device *, enum nvkm_device_type, u64 handle,
const char *name, const char *cfg, const char *dbg,
struct nvkm_device *);
int nvkm_device_init(struct nvkm_device *);
int nvkm_device_fini(struct nvkm_device *, enum nvkm_suspend_state suspend);
#endif
@@ -0,0 +1,359 @@
/*
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <core/tegra.h>
#ifdef CONFIG_NOUVEAU_PLATFORM_DRIVER
#include "priv.h"
#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
#include <asm/dma-iommu.h>
#endif
static int
nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev)
{
int ret;
if (tdev->vdd) {
ret = regulator_enable(tdev->vdd);
if (ret)
goto err_power;
}
ret = clk_prepare_enable(tdev->clk);
if (ret)
goto err_clk;
ret = clk_prepare_enable(tdev->clk_ref);
if (ret)
goto err_clk_ref;
ret = clk_prepare_enable(tdev->clk_pwr);
if (ret)
goto err_clk_pwr;
clk_set_rate(tdev->clk_pwr, 204000000);
udelay(10);
if (!tdev->pdev->dev.pm_domain) {
reset_control_assert(tdev->rst);
udelay(10);
ret = tegra_pmc_powergate_remove_clamping(tdev->pmc,
TEGRA_POWERGATE_3D);
if (ret)
goto err_clamp;
udelay(10);
reset_control_deassert(tdev->rst);
udelay(10);
}
return 0;
err_clamp:
clk_disable_unprepare(tdev->clk_pwr);
err_clk_pwr:
clk_disable_unprepare(tdev->clk_ref);
err_clk_ref:
clk_disable_unprepare(tdev->clk);
err_clk:
if (tdev->vdd)
regulator_disable(tdev->vdd);
err_power:
return ret;
}
static int
nvkm_device_tegra_power_down(struct nvkm_device_tegra *tdev)
{
int ret;
clk_disable_unprepare(tdev->clk_pwr);
clk_disable_unprepare(tdev->clk_ref);
clk_disable_unprepare(tdev->clk);
udelay(10);
if (tdev->vdd) {
ret = regulator_disable(tdev->vdd);
if (ret)
return ret;
}
return 0;
}
static void
nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev)
{
#if IS_ENABLED(CONFIG_IOMMU_API)
struct device *dev = &tdev->pdev->dev;
unsigned long pgsize_bitmap;
int ret;
#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
if (dev->archdata.mapping) {
struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
arm_iommu_detach_device(dev);
arm_iommu_release_mapping(mapping);
}
#endif
if (!tdev->func->iommu_bit)
return;
mutex_init(&tdev->iommu.mutex);
if (device_iommu_mapped(dev)) {
tdev->iommu.domain = iommu_paging_domain_alloc(dev);
if (IS_ERR(tdev->iommu.domain))
goto error;
/*
* A IOMMU is only usable if it supports page sizes smaller
* or equal to the system's PAGE_SIZE, with a preference if
* both are equal.
*/
pgsize_bitmap = tdev->iommu.domain->pgsize_bitmap;
if (pgsize_bitmap & PAGE_SIZE) {
tdev->iommu.pgshift = PAGE_SHIFT;
} else {
tdev->iommu.pgshift = fls(pgsize_bitmap & ~PAGE_MASK);
if (tdev->iommu.pgshift == 0) {
dev_warn(dev, "unsupported IOMMU page size\n");
goto free_domain;
}
tdev->iommu.pgshift -= 1;
}
ret = iommu_attach_device(tdev->iommu.domain, dev);
if (ret)
goto free_domain;
ret = nvkm_mm_init(&tdev->iommu.mm, 0, 0,
(1ULL << tdev->func->iommu_bit) >>
tdev->iommu.pgshift, 1);
if (ret)
goto detach_device;
}
return;
detach_device:
iommu_detach_device(tdev->iommu.domain, dev);
free_domain:
iommu_domain_free(tdev->iommu.domain);
error:
tdev->iommu.domain = NULL;
tdev->iommu.pgshift = 0;
dev_err(dev, "cannot initialize IOMMU MM\n");
#endif
}
static void
nvkm_device_tegra_remove_iommu(struct nvkm_device_tegra *tdev)
{
#if IS_ENABLED(CONFIG_IOMMU_API)
if (tdev->iommu.domain) {
nvkm_mm_fini(&tdev->iommu.mm);
iommu_detach_device(tdev->iommu.domain, tdev->device.dev);
iommu_domain_free(tdev->iommu.domain);
}
#endif
}
static struct nvkm_device_tegra *
nvkm_device_tegra(struct nvkm_device *device)
{
return container_of(device, struct nvkm_device_tegra, device);
}
static struct resource *
nvkm_device_tegra_resource(struct nvkm_device *device, enum nvkm_bar_id bar)
{
struct nvkm_device_tegra *tdev = nvkm_device_tegra(device);
int idx;
switch (bar) {
case NVKM_BAR0_PRI: idx = 0; break;
case NVKM_BAR1_FB : idx = 1; break;
default:
WARN_ON(1);
return NULL;
}
return platform_get_resource(tdev->pdev, IORESOURCE_MEM, idx);
}
static resource_size_t
nvkm_device_tegra_resource_addr(struct nvkm_device *device, enum nvkm_bar_id bar)
{
struct resource *res = nvkm_device_tegra_resource(device, bar);
return res ? res->start : 0;
}
static resource_size_t
nvkm_device_tegra_resource_size(struct nvkm_device *device, enum nvkm_bar_id bar)
{
struct resource *res = nvkm_device_tegra_resource(device, bar);
return res ? resource_size(res) : 0;
}
static int
nvkm_device_tegra_irq(struct nvkm_device *device)
{
struct nvkm_device_tegra *tdev = nvkm_device_tegra(device);
return platform_get_irq_byname(tdev->pdev, "stall");
}
static void *
nvkm_device_tegra_dtor(struct nvkm_device *device)
{
struct nvkm_device_tegra *tdev = nvkm_device_tegra(device);
nvkm_device_tegra_power_down(tdev);
nvkm_device_tegra_remove_iommu(tdev);
return tdev;
}
static const struct nvkm_device_func
nvkm_device_tegra_func = {
.tegra = nvkm_device_tegra,
.dtor = nvkm_device_tegra_dtor,
.irq = nvkm_device_tegra_irq,
.resource_addr = nvkm_device_tegra_resource_addr,
.resource_size = nvkm_device_tegra_resource_size,
.cpu_coherent = false,
};
int
nvkm_device_tegra_new(const struct nvkm_device_tegra_func *func,
struct platform_device *pdev,
const char *cfg, const char *dbg,
struct nvkm_device **pdevice)
{
struct nvkm_device_tegra *tdev;
unsigned long rate;
int ret;
if (!(tdev = kzalloc_obj(*tdev)))
return -ENOMEM;
tdev->func = func;
tdev->pdev = pdev;
tdev->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tdev->regs))
return PTR_ERR(tdev->regs);
if (func->require_vdd) {
tdev->vdd = devm_regulator_get(&pdev->dev, "vdd");
if (IS_ERR(tdev->vdd)) {
ret = PTR_ERR(tdev->vdd);
goto free;
}
}
tdev->rst = devm_reset_control_get(&pdev->dev, "gpu");
if (IS_ERR(tdev->rst)) {
ret = PTR_ERR(tdev->rst);
goto free;
}
tdev->clk = devm_clk_get(&pdev->dev, "gpu");
if (IS_ERR(tdev->clk)) {
ret = PTR_ERR(tdev->clk);
goto free;
}
rate = clk_get_rate(tdev->clk);
if (rate == 0) {
ret = clk_set_rate(tdev->clk, ULONG_MAX);
if (ret < 0)
goto free;
rate = clk_get_rate(tdev->clk);
dev_dbg(&pdev->dev, "GPU clock set to %lu\n", rate);
}
if (func->require_ref_clk)
tdev->clk_ref = devm_clk_get(&pdev->dev, "ref");
if (IS_ERR(tdev->clk_ref)) {
ret = PTR_ERR(tdev->clk_ref);
goto free;
}
tdev->clk_pwr = devm_clk_get(&pdev->dev, "pwr");
if (IS_ERR(tdev->clk_pwr)) {
ret = PTR_ERR(tdev->clk_pwr);
goto free;
}
tdev->pmc = devm_tegra_pmc_get(&pdev->dev);
if (IS_ERR(tdev->pmc)) {
ret = PTR_ERR(tdev->pmc);
goto free;
}
/**
* The IOMMU bit defines the upper limit of the GPU-addressable space.
*/
ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(tdev->func->iommu_bit));
if (ret)
goto free;
nvkm_device_tegra_probe_iommu(tdev);
ret = nvkm_device_tegra_power_up(tdev);
if (ret)
goto remove;
tdev->gpu_speedo = tegra_sku_info.gpu_speedo_value;
tdev->gpu_speedo_id = tegra_sku_info.gpu_speedo_id;
ret = nvkm_device_ctor(&nvkm_device_tegra_func, NULL, &pdev->dev,
NVKM_DEVICE_TEGRA, pdev->id, NULL,
cfg, dbg, &tdev->device);
if (ret)
goto powerdown;
*pdevice = &tdev->device;
return 0;
powerdown:
nvkm_device_tegra_power_down(tdev);
remove:
nvkm_device_tegra_remove_iommu(tdev);
free:
kfree(tdev);
return ret;
}
#else
int
nvkm_device_tegra_new(const struct nvkm_device_tegra_func *func,
struct platform_device *pdev,
const char *cfg, const char *dbg,
struct nvkm_device **pdevice)
{
return -ENOSYS;
}
#endif
@@ -0,0 +1,349 @@
/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#define nvkm_udevice(p) container_of((p), struct nvkm_udevice, object)
#include "priv.h"
#include "ctrl.h"
#include <core/client.h>
#include <subdev/fb.h>
#include <subdev/instmem.h>
#include <subdev/timer.h>
#include <nvif/class.h>
#include <nvif/cl0080.h>
#include <nvif/unpack.h>
struct nvkm_udevice {
struct nvkm_object object;
struct nvkm_device *device;
};
static int
nvkm_udevice_info_subdev(struct nvkm_device *device, u64 mthd, u64 *data)
{
struct nvkm_subdev *subdev;
enum nvkm_subdev_type type;
switch (mthd & NV_DEVICE_INFO_UNIT) {
case NV_DEVICE_HOST(0): type = NVKM_ENGINE_FIFO; break;
default:
return -EINVAL;
}
subdev = nvkm_device_subdev(device, type, 0);
if (subdev)
return nvkm_subdev_info(subdev, mthd, data);
return -ENODEV;
}
static void
nvkm_udevice_info_v1(struct nvkm_device *device,
struct nv_device_info_v1_data *args)
{
if (args->mthd & NV_DEVICE_INFO_UNIT) {
if (nvkm_udevice_info_subdev(device, args->mthd, &args->data))
args->mthd = NV_DEVICE_INFO_INVALID;
return;
}
args->mthd = NV_DEVICE_INFO_INVALID;
}
static int
nvkm_udevice_info(struct nvkm_udevice *udev, void *data, u32 size)
{
struct nvkm_object *object = &udev->object;
struct nvkm_device *device = udev->device;
struct nvkm_fb *fb = device->fb;
struct nvkm_instmem *imem = device->imem;
union {
struct nv_device_info_v0 v0;
struct nv_device_info_v1 v1;
} *args = data;
int ret = -ENOSYS, i;
nvif_ioctl(object, "device info size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) {
nvif_ioctl(object, "device info vers %d count %d\n",
args->v1.version, args->v1.count);
if (args->v1.count * sizeof(args->v1.data[0]) == size) {
for (i = 0; i < args->v1.count; i++)
nvkm_udevice_info_v1(device, &args->v1.data[i]);
return 0;
}
return -EINVAL;
} else
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(object, "device info vers %d\n", args->v0.version);
} else
return ret;
switch (device->chipset) {
case 0x01a:
case 0x01f:
case 0x04c:
case 0x04e:
case 0x063:
case 0x067:
case 0x068:
case 0x0aa:
case 0x0ac:
case 0x0af:
args->v0.platform = NV_DEVICE_INFO_V0_IGP;
break;
default:
switch (device->type) {
case NVKM_DEVICE_PCI:
args->v0.platform = NV_DEVICE_INFO_V0_PCI;
break;
case NVKM_DEVICE_AGP:
args->v0.platform = NV_DEVICE_INFO_V0_AGP;
break;
case NVKM_DEVICE_PCIE:
args->v0.platform = NV_DEVICE_INFO_V0_PCIE;
break;
case NVKM_DEVICE_TEGRA:
args->v0.platform = NV_DEVICE_INFO_V0_SOC;
break;
default:
WARN_ON(1);
break;
}
break;
}
switch (device->card_type) {
case NV_04: args->v0.family = NV_DEVICE_INFO_V0_TNT; break;
case NV_10:
case NV_11: args->v0.family = NV_DEVICE_INFO_V0_CELSIUS; break;
case NV_20: args->v0.family = NV_DEVICE_INFO_V0_KELVIN; break;
case NV_30: args->v0.family = NV_DEVICE_INFO_V0_RANKINE; break;
case NV_40: args->v0.family = NV_DEVICE_INFO_V0_CURIE; break;
case NV_50: args->v0.family = NV_DEVICE_INFO_V0_TESLA; break;
case NV_C0: args->v0.family = NV_DEVICE_INFO_V0_FERMI; break;
case NV_E0: args->v0.family = NV_DEVICE_INFO_V0_KEPLER; break;
case GM100: args->v0.family = NV_DEVICE_INFO_V0_MAXWELL; break;
case GP100: args->v0.family = NV_DEVICE_INFO_V0_PASCAL; break;
case GV100: args->v0.family = NV_DEVICE_INFO_V0_VOLTA; break;
case TU100: args->v0.family = NV_DEVICE_INFO_V0_TURING; break;
case GA100: args->v0.family = NV_DEVICE_INFO_V0_AMPERE; break;
case AD100: args->v0.family = NV_DEVICE_INFO_V0_ADA; break;
case GH100: args->v0.family = NV_DEVICE_INFO_V0_HOPPER; break;
case GB10x: args->v0.family = NV_DEVICE_INFO_V0_BLACKWELL; break;
case GB20x: args->v0.family = NV_DEVICE_INFO_V0_BLACKWELL; break;
default:
args->v0.family = 0;
break;
}
args->v0.chipset = device->chipset;
args->v0.revision = device->chiprev;
if (fb && fb->ram)
args->v0.ram_size = args->v0.ram_user = fb->ram->size;
else
args->v0.ram_size = args->v0.ram_user = 0;
if (imem && args->v0.ram_size > 0)
args->v0.ram_user = args->v0.ram_user - imem->reserved;
snprintf(args->v0.chip, sizeof(args->v0.chip), "%s", device->chip->name);
snprintf(args->v0.name, sizeof(args->v0.name), "%s", device->name);
return 0;
}
static int
nvkm_udevice_time(struct nvkm_udevice *udev, void *data, u32 size)
{
struct nvkm_object *object = &udev->object;
struct nvkm_device *device = udev->device;
union {
struct nv_device_time_v0 v0;
} *args = data;
int ret = -ENOSYS;
nvif_ioctl(object, "device time size %d\n", size);
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
nvif_ioctl(object, "device time vers %d\n", args->v0.version);
args->v0.time = nvkm_timer_read(device->timer);
}
return ret;
}
static int
nvkm_udevice_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
{
struct nvkm_udevice *udev = nvkm_udevice(object);
nvif_ioctl(object, "device mthd %08x\n", mthd);
switch (mthd) {
case NV_DEVICE_V0_INFO:
return nvkm_udevice_info(udev, data, size);
case NV_DEVICE_V0_TIME:
return nvkm_udevice_time(udev, data, size);
default:
break;
}
return -EINVAL;
}
static int
nvkm_udevice_map(struct nvkm_object *object, void *argv, u32 argc,
enum nvkm_object_map *type, u64 *addr, u64 *size)
{
struct nvkm_udevice *udev = nvkm_udevice(object);
struct nvkm_device *device = udev->device;
*type = NVKM_OBJECT_MAP_IO;
*addr = device->func->resource_addr(device, NVKM_BAR0_PRI);
*size = device->func->resource_size(device, NVKM_BAR0_PRI);
return 0;
}
static int
nvkm_udevice_fini(struct nvkm_object *object, enum nvkm_suspend_state suspend)
{
struct nvkm_udevice *udev = nvkm_udevice(object);
struct nvkm_device *device = udev->device;
int ret = 0;
mutex_lock(&device->mutex);
if (!--device->refcount) {
ret = nvkm_device_fini(device, suspend);
if (ret && suspend) {
device->refcount++;
goto done;
}
}
done:
mutex_unlock(&device->mutex);
return ret;
}
static int
nvkm_udevice_init(struct nvkm_object *object)
{
struct nvkm_udevice *udev = nvkm_udevice(object);
struct nvkm_device *device = udev->device;
int ret = 0;
mutex_lock(&device->mutex);
if (!device->refcount++) {
ret = nvkm_device_init(device);
if (ret) {
device->refcount--;
goto done;
}
}
done:
mutex_unlock(&device->mutex);
return ret;
}
static int
nvkm_udevice_child_new(const struct nvkm_oclass *oclass,
void *data, u32 size, struct nvkm_object **pobject)
{
struct nvkm_udevice *udev = nvkm_udevice(oclass->parent);
const struct nvkm_device_oclass *sclass = oclass->priv;
return sclass->ctor(udev->device, oclass, data, size, pobject);
}
static int
nvkm_udevice_child_get(struct nvkm_object *object, int index,
struct nvkm_oclass *oclass)
{
struct nvkm_udevice *udev = nvkm_udevice(object);
struct nvkm_device *device = udev->device;
struct nvkm_engine *engine;
u64 mask = (1ULL << NVKM_ENGINE_DMAOBJ) |
(1ULL << NVKM_ENGINE_FIFO) |
(1ULL << NVKM_ENGINE_DISP);
const struct nvkm_device_oclass *sclass = NULL;
int i;
for (; i = __ffs64(mask), mask && !sclass; mask &= ~(1ULL << i)) {
if (!(engine = nvkm_device_engine(device, i, 0)) ||
!(engine->func->base.sclass))
continue;
oclass->engine = engine;
index -= engine->func->base.sclass(oclass, index, &sclass);
}
if (!sclass) {
if (index-- == 0)
sclass = &nvkm_control_oclass;
else if (device->mmu && index-- == 0)
sclass = &device->mmu->user;
else if (device->fault && index-- == 0)
sclass = &device->fault->user;
else if (device->vfn && index-- == 0)
sclass = &device->vfn->user;
else
return -EINVAL;
oclass->base = sclass->base;
oclass->engine = NULL;
}
oclass->ctor = nvkm_udevice_child_new;
oclass->priv = sclass;
return 0;
}
static const struct nvkm_object_func
nvkm_udevice = {
.init = nvkm_udevice_init,
.fini = nvkm_udevice_fini,
.mthd = nvkm_udevice_mthd,
.map = nvkm_udevice_map,
.sclass = nvkm_udevice_child_get,
};
static int
nvkm_udevice_new(const struct nvkm_oclass *oclass, void *data, u32 size,
struct nvkm_object **pobject)
{
struct nvkm_client *client = oclass->client;
struct nvkm_udevice *udev;
if (!(udev = kzalloc_obj(*udev)))
return -ENOMEM;
nvkm_object_ctor(&nvkm_udevice, oclass, &udev->object);
*pobject = &udev->object;
/* find the device that matches what the client requested */
udev->device = nvkm_device_find(client->device);
if (!udev->device)
return -ENODEV;
return 0;
}
const struct nvkm_sclass
nvkm_udevice_sclass = {
.oclass = NV_DEVICE,
.minver = 0,
.maxver = 0,
.ctor = nvkm_udevice_new,
};