diff --git a/src/add-ons/kernel/drivers/power/Jamfile b/src/add-ons/kernel/drivers/power/Jamfile index 207f29b50d..4c3c6ad964 100644 --- a/src/add-ons/kernel/drivers/power/Jamfile +++ b/src/add-ons/kernel/drivers/power/Jamfile @@ -5,4 +5,5 @@ SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_battery ; SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_button ; SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_lid ; SubInclude HAIKU_TOP src add-ons kernel drivers power acpi_thermal ; +SubInclude HAIKU_TOP src add-ons kernel drivers power pch_thermal ; SubInclude HAIKU_TOP src add-ons kernel drivers power x86_cpuidle ; diff --git a/src/add-ons/kernel/drivers/power/pch_thermal/AreaKeeper.h b/src/add-ons/kernel/drivers/power/pch_thermal/AreaKeeper.h new file mode 100644 index 0000000000..83861397f2 --- /dev/null +++ b/src/add-ons/kernel/drivers/power/pch_thermal/AreaKeeper.h @@ -0,0 +1,76 @@ +/* + * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Axel Dörfler, axeld@pinc-software.de + */ +#ifndef AREA_KEEPER_H +#define AREA_KEEPER_H + + +#include +#include + +#include + + +class AreaKeeper { + public: + AreaKeeper(); + ~AreaKeeper(); + + area_id Create(const char *name, void **_virtualAddress, uint32 spec, + size_t size, uint32 lock, uint32 protection); + area_id Map(const char *name, phys_addr_t physicalAddress, + size_t numBytes, uint32 spec, uint32 protection, + void **_virtualAddress); + + status_t InitCheck() { return fArea < B_OK ? (status_t)fArea : B_OK; } + void Detach(); + + private: + area_id fArea; +}; + + +AreaKeeper::AreaKeeper() + : + fArea(-1) +{ +} + + +AreaKeeper::~AreaKeeper() +{ + if (fArea >= B_OK) + delete_area(fArea); +} + + +area_id +AreaKeeper::Create(const char *name, void **_virtualAddress, uint32 spec, + size_t size, uint32 lock, uint32 protection) +{ + fArea = create_area(name, _virtualAddress, spec, size, lock, protection); + return fArea; +} + + +area_id +AreaKeeper::Map(const char *name, phys_addr_t physicalAddress, size_t numBytes, + uint32 spec, uint32 protection, void **_virtualAddress) +{ + fArea = map_physical_memory(name, physicalAddress, numBytes, spec, + protection, _virtualAddress); + return fArea; +} + + +void +AreaKeeper::Detach() +{ + fArea = -1; +} + +#endif // AREA_KEEPER_H diff --git a/src/add-ons/kernel/drivers/power/pch_thermal/Jamfile b/src/add-ons/kernel/drivers/power/pch_thermal/Jamfile new file mode 100644 index 0000000000..5eafef8823 --- /dev/null +++ b/src/add-ons/kernel/drivers/power/pch_thermal/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel drivers power pch_thermal ; + +UsePrivateKernelHeaders ; + +KernelAddon pch_thermal : + pch_thermal.cpp + ; + diff --git a/src/add-ons/kernel/drivers/power/pch_thermal/pch_thermal.cpp b/src/add-ons/kernel/drivers/power/pch_thermal/pch_thermal.cpp new file mode 100644 index 0000000000..c61fc074f3 --- /dev/null +++ b/src/add-ons/kernel/drivers/power/pch_thermal/pch_thermal.cpp @@ -0,0 +1,412 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * + * Distributed under the terms of the MIT License. + * + * PCH Thermal driver. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "AreaKeeper.h" +#include "pch_thermal.h" + + +#define PCH_THERMAL_MODULE_NAME "drivers/power/pch_thermal/driver_v1" + +#define PCH_THERMAL_DEVICE_MODULE_NAME "drivers/power/pch_thermal/device_v1" + +/* Base Namespace devices are published to */ +#define PCH_THERMAL_BASENAME "power/pch_thermal/%d" + +// name of pnp generator of path ids +#define PCH_THERMAL_PATHID_GENERATOR "pch_thermal/path_id" + +static device_manager_info *sDeviceManager; + + +//#define TRACE_PCH_THERMAL +#ifdef TRACE_PCH_THERMAL +# define TRACE(x...) dprintf("pch_thermal: " x) +#else +# define TRACE(x...) +#endif +#define ERROR(x...) dprintf("pch_thermal: " x) + + +#define write8(address, data) \ + (*((volatile uint8*)(address)) = (data)) +#define read8(address) \ + (*((volatile uint8*)(address))) +#define write16(address, data) \ + (*((volatile uint16*)(address)) = (data)) +#define read16(address) \ + (*((volatile uint16*)(address))) +#define write32(address, data) \ + (*((volatile uint32*)(address)) = (data)) +#define read32(address) \ + (*((volatile uint32*)(address))) + + +typedef struct pch_thermal_device_info { + device_node *node; + pci_device_module_info *pci; + pci_device *pci_cookie; + + addr_t registers; + area_id registers_area; + + uint32 criticalTemp; + uint32 hotTemp; + uint32 passiveTemp; +} pch_thermal_device_info; + + +status_t pch_thermal_control(void* _cookie, uint32 op, void* arg, size_t len); + + +static status_t +pch_thermal_open(void *_cookie, const char *path, int flags, void** cookie) +{ + pch_thermal_device_info *device = (pch_thermal_device_info *)_cookie; + TRACE("pch_thermal_open %p\n", device); + *cookie = device; + return B_OK; +} + + +static status_t +pch_thermal_read(void* _cookie, off_t position, void *buf, size_t* num_bytes) +{ + pch_thermal_device_info* device = (pch_thermal_device_info*)_cookie; + TRACE("pch_thermal_read %p\n", device); + pch_thermal_type therm_info; + if (*num_bytes < 1) + return B_IO_ERROR; + + if (position == 0) { + size_t max_len = *num_bytes; + char *str = (char *)buf; + TRACE("pch_thermal: read()\n"); + pch_thermal_control(device, drvOpGetThermalType, &therm_info, 0); + + snprintf(str, max_len, " Critical Temperature: %" B_PRIu32 ".%" + B_PRIu32 " C\n", (therm_info.critical_temp / 10), + (therm_info.critical_temp % 10)); + + max_len -= strlen(str); + str += strlen(str); + snprintf(str, max_len, " Current Temperature: %" B_PRIu32 ".%" + B_PRIu32 " C\n", (therm_info.current_temp / 10), + (therm_info.current_temp % 10)); + + if (therm_info.hot_temp > 0) { + max_len -= strlen(str); + str += strlen(str); + snprintf(str, max_len, " Hot Temperature: %" B_PRIu32 ".%" + B_PRIu32 " C\n", (therm_info.hot_temp / 10), + (therm_info.hot_temp % 10)); + } + *num_bytes = strlen((char *)buf); + } else { + *num_bytes = 0; + } + + return B_OK; +} + + +static status_t +pch_thermal_write(void* cookie, off_t position, const void* buffer, + size_t* num_bytes) +{ + return B_ERROR; +} + + +status_t +pch_thermal_control(void* _cookie, uint32 op, void* arg, size_t len) +{ + pch_thermal_device_info* device = (pch_thermal_device_info*)_cookie; + status_t err = B_ERROR; + + pch_thermal_type *att = NULL; + + switch (op) { + case drvOpGetThermalType: { + att = (pch_thermal_type *)arg; + + // Read basic temperature thresholds. + att->critical_temp = device->criticalTemp; + att->hot_temp = device->hotTemp; + + uint16 temp = read16(device->registers + PCH_THERMAL_TEMP); + temp = (temp >> PCH_THERMAL_TEMP_TSR_SHIFT) + & PCH_THERMAL_TEMP_TSR_MASK; + att->current_temp = (uint32)temp * 10 / 2 - 500; + + err = B_OK; + break; + } + } + return err; +} + + +static status_t +pch_thermal_close (void* cookie) +{ + return B_OK; +} + + +static status_t +pch_thermal_free (void* cookie) +{ + return B_OK; +} + + +// #pragma mark - driver module API + + +static float +pch_thermal_support(device_node *parent) +{ + const char *bus; + + // make sure parent is really the PCI bus manager + if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) + return -1; + + if (strcmp(bus, "pci")) + return 0.0; + + uint16 vendorID; + uint16 deviceID; + + // get vendor and device ID + if (sDeviceManager->get_attr_uint16(parent, B_DEVICE_VENDOR_ID, &vendorID, + false) != B_OK + || sDeviceManager->get_attr_uint16(parent, B_DEVICE_ID, &deviceID, + false) != B_OK) { + return -1; + } + + // check whether it's really a PCH thermal device + if (vendorID != 0x8086) + return 0.0; + + const uint16 devices[] = { 0x9c24, 0x8c24, 0x9ca4, 0x9d31, 0xa131, 0x9df9, + 0xa379, 0x06f9, 0 }; + for (const uint16* device = devices; *device != 0; device++) { + if (*device == deviceID) + return 0.6; + } + + return 0.0; +} + + +static status_t +pch_thermal_register_device(device_node *node) +{ + device_attr attrs[] = { + { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "PCH Thermal" }}, + { NULL } + }; + + return sDeviceManager->register_node(node, PCH_THERMAL_MODULE_NAME, attrs, + NULL, NULL); +} + + +static status_t +pch_thermal_init_driver(device_node *node, void **_driverCookie) +{ + *_driverCookie = node; + + return B_OK; +} + + +static void +pch_thermal_uninit_driver(void *driverCookie) +{ +} + + +static status_t +pch_thermal_register_child_devices(void *_cookie) +{ + device_node *node = (device_node*)_cookie; + int path_id; + char name[128]; + + path_id = sDeviceManager->create_id(PCH_THERMAL_PATHID_GENERATOR); + if (path_id < 0) { + ERROR("pch_thermal_register_child_devices: couldn't create a path_id" + "\n"); + return B_ERROR; + } + + snprintf(name, sizeof(name), PCH_THERMAL_BASENAME, path_id); + + return sDeviceManager->publish_device(node, name, + PCH_THERMAL_DEVICE_MODULE_NAME); +} + + +static status_t +pch_thermal_init_device(void *_cookie, void **cookie) +{ + device_node *node = (device_node *)_cookie; + pch_thermal_device_info *device; + device_node *parent; + status_t status = B_NO_INIT; + + device = (pch_thermal_device_info *)calloc(1, sizeof(*device)); + if (device == NULL) + return B_NO_MEMORY; + + device->node = node; + + parent = sDeviceManager->get_parent_node(node); + sDeviceManager->get_driver(parent, (driver_module_info **)&device->pci, + (void **)&device->pci_cookie); + sDeviceManager->put_node(parent); + + struct pci_info info; + device->pci->get_pci_info(device->pci_cookie, &info); + + // map the registers (low + high for 64-bit when requested) + phys_addr_t physicalAddress = info.u.h0.base_registers[0]; + physicalAddress &= PCI_address_memory_32_mask; + if ((info.u.h0.base_register_flags[0] & 0xc) == PCI_address_type_64) + physicalAddress += (phys_addr_t)info.u.h0.base_registers[1] << 32; + + size_t mapSize = info.u.h0.base_register_sizes[0]; + + AreaKeeper mmioMapper; + device->registers_area = mmioMapper.Map("intel PCH thermal mmio", + physicalAddress, mapSize, B_ANY_KERNEL_ADDRESS, + B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&device->registers); + + if (mmioMapper.InitCheck() < B_OK) { + ERROR("could not map memory I/O!\n"); + status = device->registers_area; + goto err; + } + + { + bool enabled = false; + uint8 tsel = read8(device->registers + PCH_THERMAL_TSEL); + if ((tsel & PCH_THERMAL_TSEL_ETS) != 0) + enabled = true; + if (!enabled) { + if ((tsel & PCH_THERMAL_TSEL_PLDB) != 0) { + status = B_DEVICE_NOT_FOUND; + goto err; + } + + write8(device->registers + PCH_THERMAL_TSEL, + tsel | PCH_THERMAL_TSEL_ETS); + + if ((tsel & PCH_THERMAL_TSEL_ETS) == 0) { + status = B_DEVICE_NOT_FOUND; + goto err; + } + } + + // read config temperatures + uint16 ctt = read16(device->registers + PCH_THERMAL_CTT); + ctt = (ctt >> PCH_THERMAL_CTT_CTRIP_SHIFT) & PCH_THERMAL_CTT_CTRIP_MASK; + device->criticalTemp = (ctt != 0) ? (uint32)ctt * 10 / 2 - 500 : 0; + + uint16 phl = read16(device->registers + PCH_THERMAL_PHL); + phl = (phl >> PCH_THERMAL_PHL_PHLL_SHIFT) & PCH_THERMAL_PHL_PHLL_MASK; + device->hotTemp = (phl != 0) ? (uint32)phl * 10 / 2 - 500 : 0; + } + TRACE("pch_thermal_init_device %p\n", device); + + mmioMapper.Detach(); + *cookie = device; + return B_OK; + +err: + free(device); + return status; +} + + +static void +pch_thermal_uninit_device(void *_cookie) +{ + pch_thermal_device_info *device = (pch_thermal_device_info *)_cookie; + TRACE("pch_thermal_uninit_device %p\n", device); + delete_area(device->registers_area); + free(device); +} + + + +module_dependency module_dependencies[] = { + { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, + {} +}; + + +driver_module_info pch_thermal_driver_module = { + { + PCH_THERMAL_MODULE_NAME, + 0, + NULL + }, + + pch_thermal_support, + pch_thermal_register_device, + pch_thermal_init_driver, + pch_thermal_uninit_driver, + pch_thermal_register_child_devices, + NULL, // rescan + NULL, // removed +}; + + +struct device_module_info pch_thermal_device_module = { + { + PCH_THERMAL_DEVICE_MODULE_NAME, + 0, + NULL + }, + + pch_thermal_init_device, + pch_thermal_uninit_device, + NULL, + + pch_thermal_open, + pch_thermal_close, + pch_thermal_free, + pch_thermal_read, + pch_thermal_write, + NULL, + pch_thermal_control, + + NULL, + NULL +}; + +module_info *modules[] = { + (module_info *)&pch_thermal_driver_module, + (module_info *)&pch_thermal_device_module, + NULL +}; diff --git a/src/add-ons/kernel/drivers/power/pch_thermal/pch_thermal.h b/src/add-ons/kernel/drivers/power/pch_thermal/pch_thermal.h new file mode 100644 index 0000000000..b2bbf46de7 --- /dev/null +++ b/src/add-ons/kernel/drivers/power/pch_thermal/pch_thermal.h @@ -0,0 +1,94 @@ +/* + * Copyright 2020, Jérôme Duval, jerome.duval@gmail.com. + * + * Distributed under the terms of the MIT License. + */ +#ifndef _PCH_THERMAL_H +#define _PCH_THERMAL_H + + +enum { /* ioctl op-codes */ + drvOpGetThermalType = B_DEVICE_OP_CODES_END + 10001, +}; + + +struct pch_thermal_type { + /* Required fields for thermal devices */ + uint32 critical_temp; + uint32 current_temp; + + /* Optional HOT temp, S4 sleep threshold */ + uint32 hot_temp; +}; + +// Registers +#define PCH_THERMAL_TEMP 0x00 +#define PCH_THERMAL_TEMP_TSR_SHIFT 0 +#define PCH_THERMAL_TEMP_TSR_MASK 0xff +#define PCH_THERMAL_TSC 0x04 +#define PCH_THERMAL_TSC_CPDE (1 << 0) +#define PCH_THERMAL_TSC_PLDB (1 << 7) +#define PCH_THERMAL_TSS 0x06 +#define PCH_THERMAL_TSS_SMIS (1 << 2) +#define PCH_THERMAL_TSS_GPES (1 << 3) +#define PCH_THERMAL_TSS_TSDSS (1 << 4) +#define PCH_THERMAL_TSEL 0x08 +#define PCH_THERMAL_TSEL_ETS (1 << 0) +#define PCH_THERMAL_TSEL_PLDB (1 << 7) +#define PCH_THERMAL_TSREL 0x0a +#define PCH_THERMAL_TSREL_ESTR (1 << 0) +#define PCH_THERMAL_TSREL_PLDB (1 << 7) +#define PCH_THERMAL_TSMIC 0x0c +#define PCH_THERMAL_TSMIC_ATST (1 << 0) +#define PCH_THERMAL_TSMIC_PLDB (1 << 7) +#define PCH_THERMAL_CTT 0x10 +#define PCH_THERMAL_CTT_CTRIP_SHIFT 0 +#define PCH_THERMAL_CTT_CTRIP_MASK 0x1ff +#define PCH_THERMAL_TAHV 0x14 +#define PCH_THERMAL_TAHV_AH_SHIFT 0 +#define PCH_THERMAL_TAHV_AH_MASK 0x1ff +#define PCH_THERMAL_TALV 0x18 +#define PCH_THERMAL_TALV_AL_SHIFT 0 +#define PCH_THERMAL_TALV_AL_MASK 0x1ff +#define PCH_THERMAL_TSPM 0x1c +#define PCH_THERMAL_TSPM_LTT_SHIFT 0 +#define PCH_THERMAL_TSPM_LTT_MASK 0x1ff +#define PCH_THERMAL_TSPM_MAXTSST_SHIFT 9 +#define PCH_THERMAL_TSPM_MAXTSST_MASK 0xf +#define PCH_THERMAL_TSPM_DTSSIC0 (1 << 13) +#define PCH_THERMAL_TSPM_DTSSS0EN (1 << 14) +#define PCH_THERMAL_TSPM_TSPMLOCK (1 << 15) +#define PCH_THERMAL_TL 0x40 +#define PCH_THERMAL_TL_T0L_SHIFT 0 +#define PCH_THERMAL_TL_T0L_MASK 0x1ff +#define PCH_THERMAL_TL_T1L_SHIFT 10 +#define PCH_THERMAL_TL_T1L_MASK 0x1ff +#define PCH_THERMAL_TL_T2L_SHIFT 20 +#define PCH_THERMAL_TL_T2L_MASK 0x1ff +#define PCH_THERMAL_TL_TTEN (1 << 29) +#define PCH_THERMAL_TL_TT13EN (1 << 30) +#define PCH_THERMAL_TL_TTL (1 << 31) +#define PCH_THERMAL_TL2 0x50 +#define PCH_THERMAL_TL2_TL2LOCK_SHIFT 15 +#define PCH_THERMAL_TL2_PCMTEN_SHIFT 16 +#define PCH_THERMAL_PHL 0x60 +#define PCH_THERMAL_PHL_PHLL_SHIFT 0 +#define PCH_THERMAL_PHL_PHLL_MASK 0x1ff +#define PCH_THERMAL_PHL_PHLE (1 << 15) +#define PCH_THERMAL_PHLC 0x62 +#define PCH_THERMAL_PHLC_PHLL (1 << 0) +#define PCH_THERMAL_TAS 0x80 +#define PCH_THERMAL_TAS_ALHE (1 << 0) +#define PCH_THERMAL_TAS_AHLE (1 << 1) +#define PCH_THERMAL_TSPIEN 0x82 +#define PCH_THERMAL_TSPIEN_ALHEN (1 << 0) +#define PCH_THERMAL_TSPIEN_AHLEN (1 << 1) +#define PCH_THERMAL_TSGPEN 0x84 +#define PCH_THERMAL_TSGPEN_ALHEN (1 << 0) +#define PCH_THERMAL_TSGPEN_AHLEN (1 << 1) +#define PCH_THERMAL_TCFD 0xf0 +#define PCH_THERMAL_TCFD_TCD (1 << 0) + + +#endif // _PCH_THERMAL_H +