static int acpi_tz_cooling_thread_start(struct acpi_tz_softc *sc) { int error; ACPI_LOCK(thermal); if (sc->tz_cooling_proc_running) { ACPI_UNLOCK(thermal); return (0); } sc->tz_cooling_proc_running = TRUE; ACPI_UNLOCK(thermal); error = 0; if (sc->tz_cooling_proc == NULL) { error = kproc_create(acpi_tz_cooling_thread, sc, &sc->tz_cooling_proc, RFHIGHPID, 0, "acpi_cooling%d", device_get_unit(sc->tz_dev)); if (error != 0) { device_printf(sc->tz_dev, "could not create thread - %d", error); ACPI_LOCK(thermal); sc->tz_cooling_proc_running = FALSE; ACPI_UNLOCK(thermal); } } return (error); }
/* * Thermal zone monitor thread. */ static void acpi_tz_thread(void *arg) { device_t *devs; int devcount, i; int flags; struct acpi_tz_softc **sc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); devs = NULL; devcount = 0; sc = NULL; for (;;) { /* If the number of devices has changed, re-evaluate. */ if (devclass_get_count(acpi_tz_devclass) != devcount) { if (devs != NULL) { free(devs, M_TEMP); free(sc, M_TEMP); } devclass_get_devices(acpi_tz_devclass, &devs, &devcount); sc = malloc(sizeof(struct acpi_tz_softc *) * devcount, M_TEMP, M_WAITOK | M_ZERO); for (i = 0; i < devcount; i++) sc[i] = device_get_softc(devs[i]); } /* Check for temperature events and act on them. */ for (i = 0; i < devcount; i++) { ACPI_LOCK(thermal); flags = sc[i]->tz_flags; sc[i]->tz_flags &= TZ_FLAG_NO_SCP; ACPI_UNLOCK(thermal); acpi_tz_timeout(sc[i], flags); } /* If more work to do, don't go to sleep yet. */ ACPI_LOCK(thermal); for (i = 0; i < devcount; i++) { if (sc[i]->tz_flags & ~TZ_FLAG_NO_SCP) break; } /* * If we have no more work, sleep for a while, setting PDROP so that * the mutex will not be reacquired. Otherwise, drop the mutex and * loop to handle more events. */ if (i == devcount) msleep(&acpi_tz_proc, &thermal_mutex, PZERO | PDROP, "tzpoll", hz * acpi_tz_polling_rate); else ACPI_UNLOCK(thermal); } }
static void acpi_cpu_startup_cx(struct acpi_cpu_softc *sc) { acpi_cpu_cx_list(sc); SYSCTL_ADD_STRING(&sc->cpu_sysctl_ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->cpu_dev)), OID_AUTO, "cx_supported", CTLFLAG_RD, sc->cpu_cx_supported, 0, "Cx/microsecond values for supported Cx states"); SYSCTL_ADD_PROC(&sc->cpu_sysctl_ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->cpu_dev)), OID_AUTO, "cx_lowest", CTLTYPE_STRING | CTLFLAG_RW, (void *)sc, 0, acpi_cpu_cx_lowest_sysctl, "A", "lowest Cx sleep state to use"); SYSCTL_ADD_PROC(&sc->cpu_sysctl_ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->cpu_dev)), OID_AUTO, "cx_usage", CTLTYPE_STRING | CTLFLAG_RD, (void *)sc, 0, acpi_cpu_usage_sysctl, "A", "percent usage for each Cx state"); #ifdef notyet /* Signal platform that we can handle _CST notification. */ if (!cpu_cx_generic && cpu_cst_cnt != 0) { ACPI_LOCK(acpi); AcpiOsWritePort(cpu_smi_cmd, cpu_cst_cnt, 8); ACPI_UNLOCK(acpi); } #endif }
/* Create a struct for tracking per-device suspend notification. */ static struct apm_clone_data * apm_create_clone(struct cdev *dev, struct acpi_softc *acpi_sc) { struct apm_clone_data *clone; clone = malloc(sizeof(*clone), M_APMDEV, M_WAITOK); clone->cdev = dev; clone->acpi_sc = acpi_sc; clone->notify_status = APM_EV_NONE; bzero(&clone->sel_read, sizeof(clone->sel_read)); knlist_init_mtx(&clone->sel_read.si_note, &acpi_mutex); /* * The acpi device is always managed by devd(8) and is considered * writable (i.e., ack is required to allow suspend to proceed.) */ if (strcmp("acpi", devtoname(dev)) == 0) clone->flags = ACPI_EVF_DEVD | ACPI_EVF_WRITE; else clone->flags = ACPI_EVF_NONE; ACPI_LOCK(acpi); STAILQ_INSERT_TAIL(&acpi_sc->apm_cdevs, clone, entries); ACPI_UNLOCK(acpi); return (clone); }
static void acpi_tz_signal(struct acpi_tz_softc *sc, int flags) { ACPI_LOCK(thermal); sc->tz_flags |= flags; ACPI_UNLOCK(thermal); wakeup(&acpi_tz_proc); }
static void apmreadfiltdetach(struct knote *kn) { struct apm_clone_data *clone; ACPI_LOCK(acpi); clone = kn->kn_hook; knlist_remove(&clone->sel_read.si_note, kn, 0); ACPI_UNLOCK(acpi); }
static void acpi_cst_startup(struct acpi_cst_softc *sc) { struct acpi_cpu_softc *cpu = sc->cst_parent; int i, bm_rld_done = 0; for (i = 0; i < sc->cst_cx_count; ++i) { struct acpi_cst_cx *cx = &sc->cst_cx_states[i]; int error; /* If there are C3(+) states, always enable bus master wakeup */ if (cx->type >= ACPI_STATE_C3 && !bm_rld_done && (acpi_cst_quirks & ACPI_CST_QUIRK_NO_BM) == 0) { acpi_cst_c3_bm_rld(sc); bm_rld_done = 1; } /* Redo the Cx setup, since quirks have been changed */ error = acpi_cst_cx_setup(cx); if (error) panic("C%d startup setup failed: %d", i + 1, error); } acpi_cst_support_list(sc); SYSCTL_ADD_STRING(&cpu->pcpu_sysctl_ctx, SYSCTL_CHILDREN(cpu->pcpu_sysctl_tree), OID_AUTO, "cx_supported", CTLFLAG_RD, sc->cst_cx_supported, 0, "Cx/microsecond values for supported Cx states"); SYSCTL_ADD_PROC(&cpu->pcpu_sysctl_ctx, SYSCTL_CHILDREN(cpu->pcpu_sysctl_tree), OID_AUTO, "cx_lowest", CTLTYPE_STRING | CTLFLAG_RW, (void *)sc, 0, acpi_cst_lowest_sysctl, "A", "requested lowest Cx sleep state"); SYSCTL_ADD_PROC(&cpu->pcpu_sysctl_ctx, SYSCTL_CHILDREN(cpu->pcpu_sysctl_tree), OID_AUTO, "cx_lowest_use", CTLTYPE_STRING | CTLFLAG_RD, (void *)sc, 0, acpi_cst_lowest_use_sysctl, "A", "lowest Cx sleep state to use"); SYSCTL_ADD_PROC(&cpu->pcpu_sysctl_ctx, SYSCTL_CHILDREN(cpu->pcpu_sysctl_tree), OID_AUTO, "cx_usage", CTLTYPE_STRING | CTLFLAG_RD, (void *)sc, 0, acpi_cst_usage_sysctl, "A", "percent usage for each Cx state"); #ifdef notyet /* Signal platform that we can handle _CST notification. */ if (!acpi_cst_use_fadt && acpi_cst_ctrl != 0) { ACPI_LOCK(acpi); AcpiOsWritePort(acpi_cst_smi_cmd, acpi_cst_ctrl, 8); ACPI_UNLOCK(acpi); } #endif }
static void acpi_px_startup(void *arg) { /* Signal to the platform that we are taking over CPU control. */ if (AcpiGbl_FADT.PstateControl == 0) return; ACPI_LOCK(acpi); AcpiOsWritePort(AcpiGbl_FADT.SmiCommand, AcpiGbl_FADT.PstateControl, 8); ACPI_UNLOCK(acpi); }
static int apmreadfilt(struct knote *kn, long hint) { struct apm_clone_data *clone; int sleeping; ACPI_LOCK(acpi); clone = kn->kn_hook; sleeping = clone->acpi_sc->acpi_next_sstate ? 1 : 0; ACPI_UNLOCK(acpi); return (sleeping); }
static int apmkqfilter(struct cdev *dev, struct knote *kn) { struct apm_clone_data *clone; ACPI_LOCK(acpi); clone = dev->si_drv1; kn->kn_hook = clone; kn->kn_fop = &apm_readfiltops; knlist_add(&clone->sel_read.si_note, kn, 0); ACPI_UNLOCK(acpi); return (0); }
static int apmkqfilter(struct cdev *dev, struct knote *kn) { struct apm_clone_data *clone; devfs_get_cdevpriv((void **)&clone); ACPI_LOCK(acpi); kn->kn_hook = clone; kn->kn_fop = &apm_readfiltops; knlist_add(&clone->sel_read.si_note, kn, 0); ACPI_UNLOCK(acpi); return (0); }
/* * Passive cooling thread; monitors current temperature according to the * cooling interval and calculates whether to scale back CPU frequency. */ static void acpi_tz_cooling_thread(void *arg) { struct acpi_tz_softc *sc; int error, perf, curr_temp, prev_temp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); sc = (struct acpi_tz_softc *)arg; prev_temp = sc->tz_temperature; while (sc->tz_cooling_enabled) { if (sc->tz_cooling_active) (void)acpi_tz_get_temperature(sc); curr_temp = sc->tz_temperature; if (curr_temp >= sc->tz_zone.psv) sc->tz_cooling_active = TRUE; if (sc->tz_cooling_active) { perf = sc->tz_zone.tc1 * (curr_temp - prev_temp) + sc->tz_zone.tc2 * (curr_temp - sc->tz_zone.psv); perf /= 10; if (perf != 0) { error = acpi_tz_cpufreq_update(sc, perf); /* * If error and not simply a higher priority setting was * active, disable cooling. */ if (error != 0 && error != EPERM) { device_printf(sc->tz_dev, "failed to set new freq, disabling passive cooling\n"); sc->tz_cooling_enabled = FALSE; } } } prev_temp = curr_temp; tsleep(&sc->tz_cooling_proc, PZERO, "cooling", hz * sc->tz_zone.tsp / 10); } if (sc->tz_cooling_active) { acpi_tz_cpufreq_restore(sc); sc->tz_cooling_active = FALSE; } sc->tz_cooling_proc = NULL; ACPI_LOCK(thermal); sc->tz_cooling_proc_running = FALSE; ACPI_UNLOCK(thermal); kproc_exit(0); }
static int apmpoll(struct cdev *dev, int events, struct thread *td) { struct apm_clone_data *clone; int revents; revents = 0; ACPI_LOCK(acpi); clone = dev->si_drv1; if (clone->acpi_sc->acpi_next_sstate) revents |= events & (POLLIN | POLLRDNORM); else selrecord(td, &clone->sel_read); ACPI_UNLOCK(acpi); return (revents); }
static void apmdtor(void *data) { struct apm_clone_data *clone; struct acpi_softc *acpi_sc; clone = data; acpi_sc = clone->acpi_sc; /* We are about to lose a reference so check if suspend should occur */ if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status != APM_EV_ACKED) acpi_AckSleepState(clone, 0); /* Remove this clone's data from the list and free it. */ ACPI_LOCK(acpi); STAILQ_REMOVE(&acpi_sc->apm_cdevs, clone, apm_clone_data, entries); seldrain(&clone->sel_read); knlist_destroy(&clone->sel_read.si_note); ACPI_UNLOCK(acpi); free(clone, M_APMDEV); }
static int apmclose(struct cdev *dev, int flag, int fmt, d_thread_t *td) { struct apm_clone_data *clone; struct acpi_softc *acpi_sc; clone = dev->si_drv1; acpi_sc = clone->acpi_sc; /* We are about to lose a reference so check if suspend should occur */ if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status != APM_EV_ACKED) acpi_AckSleepState(clone, 0); /* Remove this clone's data from the list and free it. */ ACPI_LOCK(acpi); STAILQ_REMOVE(&acpi_sc->apm_cdevs, clone, apm_clone_data, entries); knlist_destroy(&clone->sel_read.si_note); ACPI_UNLOCK(acpi); free(clone, M_APMDEV); destroy_dev_sched(dev); return (0); }
static int apmioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, struct thread *td) { int error; struct apm_clone_data *clone; struct acpi_softc *acpi_sc; struct apm_info info; struct apm_event_info *ev_info; apm_info_old_t aiop; error = 0; clone = dev->si_drv1; acpi_sc = clone->acpi_sc; switch (cmd) { case APMIO_SUSPEND: if ((flag & FWRITE) == 0) return (EPERM); if (acpi_sc->acpi_next_sstate == 0) { if (acpi_sc->acpi_suspend_sx != ACPI_STATE_S5) { error = acpi_ReqSleepState(acpi_sc, acpi_sc->acpi_suspend_sx); } else { printf( "power off via apm suspend not supported\n"); error = ENXIO; } } else error = acpi_AckSleepState(clone, 0); break; case APMIO_STANDBY: if ((flag & FWRITE) == 0) return (EPERM); if (acpi_sc->acpi_next_sstate == 0) { if (acpi_sc->acpi_standby_sx != ACPI_STATE_S5) { error = acpi_ReqSleepState(acpi_sc, acpi_sc->acpi_standby_sx); } else { printf( "power off via apm standby not supported\n"); error = ENXIO; } } else error = acpi_AckSleepState(clone, 0); break; case APMIO_NEXTEVENT: printf("apm nextevent start\n"); ACPI_LOCK(acpi); if (acpi_sc->acpi_next_sstate != 0 && clone->notify_status == APM_EV_NONE) { ev_info = (struct apm_event_info *)addr; if (acpi_sc->acpi_next_sstate <= ACPI_STATE_S3) ev_info->type = PMEV_STANDBYREQ; else ev_info->type = PMEV_SUSPENDREQ; ev_info->index = 0; clone->notify_status = APM_EV_NOTIFIED; printf("apm event returning %d\n", ev_info->type); } else error = EAGAIN; ACPI_UNLOCK(acpi); break; case APMIO_GETINFO_OLD: if (acpi_capm_get_info(&info)) error = ENXIO; aiop = (apm_info_old_t)addr; aiop->ai_major = info.ai_major; aiop->ai_minor = info.ai_minor; aiop->ai_acline = info.ai_acline; aiop->ai_batt_stat = info.ai_batt_stat; aiop->ai_batt_life = info.ai_batt_life; aiop->ai_status = info.ai_status; break; case APMIO_GETINFO: if (acpi_capm_get_info((apm_info_t)addr)) error = ENXIO; break; case APMIO_GETPWSTATUS: if (acpi_capm_get_pwstatus((apm_pwstatus_t)addr)) error = ENXIO; break; case APMIO_ENABLE: if ((flag & FWRITE) == 0) return (EPERM); apm_active = 1; break; case APMIO_DISABLE: if ((flag & FWRITE) == 0) return (EPERM); apm_active = 0; break; case APMIO_HALTCPU: break; case APMIO_NOTHALTCPU: break; case APMIO_DISPLAY: if ((flag & FWRITE) == 0) return (EPERM); break; case APMIO_BIOS: if ((flag & FWRITE) == 0) return (EPERM); bzero(addr, sizeof(struct apm_bios_arg)); break; default: error = EINVAL; break; } return (error); }