/* _NVRM_COPYRIGHT_BEGIN_ * * Copyright 2001-2002 by NVIDIA Corporation. All rights reserved. All * information contained herein is proprietary and confidential to NVIDIA * Corporation. Any use, reproduction, or disclosure without the written * permission of NVIDIA Corporation is prohibited. * * _NVRM_COPYRIGHT_END_ */ #include "nv-misc.h" #include "os-interface.h" #include "nv.h" #include "nv-freebsd.h" struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *oid_nvidia; struct sysctl_oid *oid_agp; struct sysctl_oid *oid_registry; static char *option_string = NULL; static void* nvidia_find_bridge (void); static U032 nvidia_dev_agp_cmd (device_t dev); static U032 nvidia_dev_agp_status (device_t dev); static U032 nvidia_sys_agp_cmd (device_t dev); void nvidia_sysctl_init(void) { device_t dev; struct sysctl_oid *oid; nv_parm_t *entry; sysctl_ctx_init(&sysctl_ctx); oid_nvidia = SYSCTL_ADD_NODE(&sysctl_ctx, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "nvidia", CTLFLAG_RD | CTLFLAG_DYN, 0, "NVIDIA SYSCTL Master Node"); oid_agp = SYSCTL_ADD_NODE(&sysctl_ctx, SYSCTL_CHILDREN(oid_nvidia), OID_AUTO, "agp", CTLFLAG_RD | CTLFLAG_DYN, 0, "NVIDIA SYSCTL AGP Node"); SYSCTL_ADD_STRING(&sysctl_ctx, SYSCTL_CHILDREN(oid_nvidia), OID_AUTO, "version", CTLFLAG_RD | CTLFLAG_DYN, (char *)(uintptr_t) pNVRM_ID, 0, "NVIDIA Resource Manager (NVRM) Version"); oid_registry = SYSCTL_ADD_NODE(&sysctl_ctx, SYSCTL_CHILDREN(oid_nvidia), OID_AUTO, "registry", CTLFLAG_RD | CTLFLAG_DYN, 0, "NVIDIA SYSCTL Registry Node"); entry = nv_parms; do { SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid_registry), OID_AUTO, entry->name, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_DYN, entry->data, 0, nvidia_sysctl_registry_key, "IU", NULL); entry++; } while(entry->name != NULL); option_string = malloc(1, M_NVIDIA, M_WAITOK | M_ZERO); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid_registry), OID_AUTO, "dwords", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_DYN, NULL, 0, nvidia_sysctl_registry_dwords, "A", NULL); if ((dev = nvidia_find_bridge()) != NULL) { /* * Assume this is the only AGP capable bridge in the system * and report its capabilities (hw.nvidia.agp.bridge). */ oid = SYSCTL_ADD_NODE(&sysctl_ctx, SYSCTL_CHILDREN(oid_agp), OID_AUTO, "host-bridge", CTLFLAG_RD | CTLFLAG_DYN, 0, "NVIDIA AGP Bridge Node"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rates", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_DYN, (void *) dev, 0, nvidia_sysctl_agp_rates, "A", "NVIDIA AGP Bridge Rates Info"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "fw", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_DYN, (void *) dev, 0, nvidia_sysctl_agp_fw, "A", "NVIDIA AGP Bridge FW Info"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "sba", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_DYN, (void *) dev, 0, nvidia_sysctl_agp_sba, "A", "NVIDIA AGP Bridge SBA Info"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "registers", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_DYN, (void *) dev, 0, nvidia_sysctl_agp_registers, "A", "NVIDIA AGP Bridge Registers"); } } void nvidia_sysctl_exit(void) { sysctl_ctx_free(&sysctl_ctx); if (option_string != NULL) free((void *)option_string, M_NVIDIA); } static U032 nvidia_dev_agp_cmd(device_t dev) { U008 cap_ptr; U032 tmp; cap_ptr = nvidia_pci_find_capability(dev, PCIR_CAP_ID_AGP); tmp = pci_read_config(dev, cap_ptr + 8, 4); return tmp; } static U032 nvidia_dev_agp_status(device_t dev) { U008 cap_ptr; U032 tmp; cap_ptr = nvidia_pci_find_capability(dev, PCIR_CAP_ID_AGP); tmp = pci_read_config(dev, cap_ptr + 4, 4); return tmp; } static U032 nvidia_sys_agp_cmd(device_t dev) { U032 tmp; tmp = nvidia_dev_agp_cmd(dev); tmp &= nvidia_dev_agp_cmd(nvidia_find_bridge()); return tmp; } static void* nvidia_find_bridge(void) { U008 hdrtype; U032 bus; U032 device; U032 func; device_t dev; for (bus = 0; bus < 5; bus++) { for (device = 0; device < 32; device++) { for (func = 0; func < 8; func++) { /* * We're not interested in non-bridge PCI devices, nor do * care for devices with our own or an invalid vendor ID. */ dev = pci_find_bsf(bus, device, func); if (dev == NULL) { if (func == 0) { // If a read to function zero of a specified bus/device master aborts, // then it is assumed that no such device exists on the bus since // devices are required to implement function number zero. // In this case reads to the remaining functions are not necessary. break; } else { continue; } } if (pci_get_class(dev) != PCIC_BRIDGE) break; if (pci_get_vendor(dev) == 0xffff) break; if (nvidia_pci_find_capability(dev, PCIR_CAP_ID_AGP) != 0) return dev; /* * It also doesn't make sense to to iterate over multiple * functions if this isn't a multi-function device. */ hdrtype = pci_read_config(dev, PCIR_HDRTYPE, 1); if ((hdrtype & PCIM_MFDEV) == 0) break; } } } return NULL; } int nvidia_sysctl_dev_model(SYSCTL_HANDLER_ARGS) { nv_state_t *nv = arg1; nv_stack_t *sp; char model_name[NV_DEVICE_NAME_LENGTH+1]; U016 id = nv->device_id; NV_UMA_ZONE_ALLOC_STACK(sp); if (sp == NULL) return ENOMEM; if (rm_get_device_name(sp, nv, id, NV_DEVICE_NAME_LENGTH, model_name) != RM_OK) { strcpy(model_name, "Unknown"); } NV_UMA_ZONE_FREE_STACK(sp); return SYSCTL_OUT(req, model_name, strlen(model_name) + 1); } int nvidia_sysctl_dev_vbios(SYSCTL_HANDLER_ARGS) { nv_state_t *nv = arg1; nv_stack_t *sp; U032 vbios[5]; U008 vbios_version[16]; NV_UMA_ZONE_ALLOC_STACK(sp); if (sp == NULL) return ENOMEM; if (rm_get_vbios_version(sp, nv, &vbios[0], &vbios[1], &vbios[2], &vbios[3], &vbios[4]) != RM_OK) { /* * The VBIOS version is only accessible after the device has been * initialized with rm_init_adapter. */ sprintf(vbios_version, "??.??.??.??.??"); } else { sprintf(vbios_version, "%02x.%02x.%02x.%02x.%02x", vbios[0], vbios[1], vbios[2], vbios[3], vbios[4]); } NV_UMA_ZONE_FREE_STACK(sp); return SYSCTL_OUT(req, vbios_version, strlen(vbios_version) + 1); } int nvidia_sysctl_bus_type(SYSCTL_HANDLER_ARGS) { struct nvidia_softc *sc = arg1; U008 bus_type[4]; if (nvidia_pci_find_capability(sc->dev, PCIR_CAP_ID_AGP) != 0) sprintf(bus_type, "AGP"); else if (nvidia_pci_find_capability(sc->dev, PCIR_CAP_ID_EXP) != 0) sprintf(bus_type, "PCI-E"); else sprintf(bus_type, "PCI"); return SYSCTL_OUT(req, bus_type, strlen(bus_type) + 1); } int nvidia_sysctl_registry_key(SYSCTL_HANDLER_ARGS) { int error; error = sysctl_handle_int(oidp, arg1, 0, req); if (error || !req->newptr) return error; /* refresh the registry with the updated option table */ os_registry_init(); return 0; } int nvidia_sysctl_registry_dwords(SYSCTL_HANDLER_ARGS) { int error, len; char *new_option_string; len = strlen(option_string) + 1; error = SYSCTL_OUT(req, option_string, len); if (error || !req->newptr) return error; len = (req->newlen - req->newidx); new_option_string = malloc((len + 1), M_NVIDIA, M_WAITOK); if (!new_option_string) return ENOMEM; error = SYSCTL_IN(req, new_option_string, len); if (error) return error; free(option_string, M_NVIDIA); option_string = new_option_string; option_string[len] = '\0'; nvidia_update_registry(new_option_string); return 0; } int nvidia_sysctl_agp_rates(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; U008 agp_rates[16]; U032 tmp = nvidia_dev_agp_status(dev); if ((tmp & 0x08) != 0) tmp = (tmp & 0x07) << 2; sprintf(agp_rates, "%s%s%s%s", (tmp & 0x0008) ? "8x " : "", (tmp & 0x0004) ? "4x " : "", (tmp & 0x0002) ? "2x " : "", (tmp & 0x0001) ? "1x " : ""); return SYSCTL_OUT(req, agp_rates, strlen(agp_rates) + 1); } int nvidia_sysctl_agp_fw(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; U008 agp_fw[16]; sprintf(agp_fw, "%ssupported", ((nvidia_dev_agp_status(dev) & 0x10) == 0x10) ? "" : "not "); return SYSCTL_OUT(req, agp_fw, strlen(agp_fw) + 1); } int nvidia_sysctl_agp_sba(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; U008 agp_sba[16]; sprintf(agp_sba, "%ssupported", ((nvidia_dev_agp_status(dev) & 0x200) == 0x200) ? "" : "not "); return SYSCTL_OUT(req, agp_sba, strlen(agp_sba) + 1); } int nvidia_sysctl_agp_registers(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; U008 agp_registers[24]; sprintf(agp_registers, "0x%08x:0x%08x", nvidia_dev_agp_status(dev), nvidia_dev_agp_cmd(dev)); return SYSCTL_OUT(req, agp_registers, strlen(agp_registers) + 1); } int nvidia_sysctl_agp_driver(SYSCTL_HANDLER_ARGS) { nv_state_t *nv = arg1; U008 agp_driver[24]; switch (nv->agp_config) { case NVOS_AGP_CONFIG_DISABLE_AGP: sprintf(agp_driver, "n/a (unused)"); break; case NVOS_AGP_CONFIG_OSAGP: sprintf(agp_driver, "freebsd (agp.ko)"); break; case NVOS_AGP_CONFIG_NVAGP: sprintf(agp_driver, "nvidia"); break; } return SYSCTL_OUT(req, agp_driver, strlen(agp_driver) + 1); } int nvidia_sysctl_agp_rate_status(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; U008 agp_rate[16]; U032 tmp = nvidia_sys_agp_cmd(dev); if ((tmp & 0x100) && (tmp &= 0x07)) { sprintf(agp_rate, "%1dx", (nvidia_dev_agp_status(dev) & 0x08) ? (tmp << 2) : tmp); } else { sprintf(agp_rate, "n/a (disabled)"); } return SYSCTL_OUT(req, agp_rate, strlen(agp_rate) + 1); } int nvidia_sysctl_agp_fw_status(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; U008 agp_fw[16]; U032 tmp = nvidia_sys_agp_cmd(dev); if ((tmp & 0x100) == 0x100) { sprintf(agp_fw, "%s", ((tmp & 0x10) == 0x10) ? "enabled" : "disabled"); } else { sprintf(agp_fw, "n/a (disabled)"); } return SYSCTL_OUT(req, agp_fw, strlen(agp_fw) + 1); } int nvidia_sysctl_agp_sba_status(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; U008 agp_sba[16]; U032 tmp = nvidia_sys_agp_cmd(dev); if ((tmp & 0x100) == 0x100) { sprintf(agp_sba, "%s", ((tmp & 0x200) == 0x200) ? "enabled" : "disabled"); } else { sprintf(agp_sba, "n/a (disabled)"); } return SYSCTL_OUT(req, agp_sba, strlen(agp_sba) + 1); } int nvidia_sysctl_agp_status(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; U008 agp_cmd[16]; sprintf(agp_cmd, "%s", (nvidia_sys_agp_cmd(dev) & 0x100) ? "enabled" : "disabled"); return SYSCTL_OUT(req, agp_cmd, strlen(agp_cmd) + 1); } void nv_sysctl_init(nv_state_t *nv) { struct sysctl_oid *oid; struct nvidia_softc *sc = nv->os_state; char name[4]; sprintf(name, "%d", device_get_unit(sc->dev)); sysctl_ctx_init(&sc->sysctl_ctx); oid = SYSCTL_ADD_NODE(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid_nvidia), OID_AUTO, "cards", CTLFLAG_RD | CTLFLAG_DYN, 0, "NVIDIA SYSCTL Cards Node"); oid = SYSCTL_ADD_NODE(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, name, CTLFLAG_RD | CTLFLAG_DYN, 0, "NVIDIA SYSCTL Device Node"); SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "model", CTLFLAG_RD | CTLFLAG_DYN, (void *) nv, 0, nvidia_sysctl_dev_model, "A", "NVIDIA Device Model Name"); SYSCTL_ADD_UINT(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "irq", CTLFLAG_RD | CTLFLAG_DYN, &nv->interrupt_line, 0, "NVIDIA Device IRQ Number"); SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "vbios", CTLFLAG_RD | CTLFLAG_DYN, (void *) nv, 0, nvidia_sysctl_dev_vbios, "A", "NVIDIA Device VBIOS Version"); SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "type", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc, 0, nvidia_sysctl_bus_type, "A", "NVIDIA Device Bus Type"); if (nvidia_pci_find_capability(sc->dev, PCIR_CAP_ID_AGP) != 0) { /* * Assume this is the only AGP VGA device in the system * and report its capabilities (hw.nvidia.agp.card). */ oid = SYSCTL_ADD_NODE(&sysctl_ctx, SYSCTL_CHILDREN(oid_agp), OID_AUTO, "card", CTLFLAG_RD | CTLFLAG_DYN, 0, "NVIDIA AGP Device Node"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rates", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc->dev, 0, nvidia_sysctl_agp_rates, "A", "NVIDIA AGP Device Rates Info"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "fw", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc->dev, 0, nvidia_sysctl_agp_fw, "A", "NVIDIA AGP Device FW Info"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "sba", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc->dev, 0, nvidia_sysctl_agp_sba, "A", "NVIDIA AGP Device SBA Info"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "registers", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc->dev, 0, nvidia_sysctl_agp_registers, "A", "NVIDIA AGP Device Registers"); if (nvidia_find_bridge() != NULL) { /* * If we can find a bridge and what assume to be the * only AGP VGA device, report the AGP status. */ oid = SYSCTL_ADD_NODE(&sysctl_ctx, SYSCTL_CHILDREN(oid_agp), OID_AUTO, "status", CTLFLAG_RD | CTLFLAG_DYN, 0, "NVIDIA AGP Status Node"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "status", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc->dev, 0, nvidia_sysctl_agp_status, "A", "NVIDIA AGP Status Information"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "driver", CTLFLAG_RD | CTLFLAG_DYN, (void *) nv, 0, nvidia_sysctl_agp_driver, "A", "NVIDIA AGP Driver Information"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "rate", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc->dev, 0, nvidia_sysctl_agp_rate_status, "A", "NVIDIA AGP Rate Status Information"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "fw", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc->dev, 0, nvidia_sysctl_agp_fw_status, "A", "NVIDIA AGP FW Status Information"); SYSCTL_ADD_PROC(&sysctl_ctx, SYSCTL_CHILDREN(oid), OID_AUTO, "sba", CTLFLAG_RD | CTLFLAG_DYN, (void *) sc->dev, 0, nvidia_sysctl_agp_sba_status, "A", "NVIDIA AGP SBA Status Information"); } } } void nv_sysctl_exit(nv_state_t *nv) { struct nvidia_softc *sc = nv->os_state; sysctl_ctx_free(&sc->sysctl_ctx); }