diff options
| -rw-r--r-- | drivers/thermal/int340x_thermal/Makefile | 1 | ||||
| -rw-r--r-- | drivers/thermal/int340x_thermal/processor_thermal_device.c | 298 |
2 files changed, 299 insertions, 0 deletions
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile index ffe40bffaf1a..d4413698a85f 100644 --- a/drivers/thermal/int340x_thermal/Makefile +++ b/drivers/thermal/int340x_thermal/Makefile | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o | 1 | obj-$(CONFIG_INT340X_THERMAL) += int3400_thermal.o |
| 2 | obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o | 2 | obj-$(CONFIG_INT340X_THERMAL) += int3402_thermal.o |
| 3 | obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o | 3 | obj-$(CONFIG_INT340X_THERMAL) += int3403_thermal.o |
| 4 | obj-$(CONFIG_INT340X_THERMAL) += processor_thermal_device.o | ||
| 4 | obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o | 5 | obj-$(CONFIG_ACPI_THERMAL_REL) += acpi_thermal_rel.o |
diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c new file mode 100644 index 000000000000..f83c55b09e52 --- /dev/null +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c | |||
| @@ -0,0 +1,298 @@ | |||
| 1 | /* | ||
| 2 | * processor_thermal_device.c | ||
| 3 | * Copyright (c) 2014, Intel Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | */ | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/init.h> | ||
| 18 | #include <linux/pci.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/acpi.h> | ||
| 21 | |||
| 22 | /* Broadwell-U/HSB thermal reporting device */ | ||
| 23 | #define PCI_DEVICE_ID_PROC_BDW_THERMAL 0x1603 | ||
| 24 | #define PCI_DEVICE_ID_PROC_HSB_THERMAL 0x0A03 | ||
| 25 | |||
| 26 | /* Braswell thermal reporting device */ | ||
| 27 | #define PCI_DEVICE_ID_PROC_BSW_THERMAL 0x22DC | ||
| 28 | |||
| 29 | struct power_config { | ||
| 30 | u32 index; | ||
| 31 | u32 min_uw; | ||
| 32 | u32 max_uw; | ||
| 33 | u32 tmin_us; | ||
| 34 | u32 tmax_us; | ||
| 35 | u32 step_uw; | ||
| 36 | }; | ||
| 37 | |||
| 38 | struct proc_thermal_device { | ||
| 39 | struct device *dev; | ||
| 40 | struct acpi_device *adev; | ||
| 41 | struct power_config power_limits[2]; | ||
| 42 | }; | ||
| 43 | |||
| 44 | enum proc_thermal_emum_mode_type { | ||
| 45 | PROC_THERMAL_NONE, | ||
| 46 | PROC_THERMAL_PCI, | ||
| 47 | PROC_THERMAL_PLATFORM_DEV | ||
| 48 | }; | ||
| 49 | |||
| 50 | /* | ||
| 51 | * We can have only one type of enumeration, PCI or Platform, | ||
| 52 | * not both. So we don't need instance specific data. | ||
| 53 | */ | ||
| 54 | static enum proc_thermal_emum_mode_type proc_thermal_emum_mode = | ||
| 55 | PROC_THERMAL_NONE; | ||
| 56 | |||
| 57 | #define POWER_LIMIT_SHOW(index, suffix) \ | ||
| 58 | static ssize_t power_limit_##index##_##suffix##_show(struct device *dev, \ | ||
| 59 | struct device_attribute *attr, \ | ||
| 60 | char *buf) \ | ||
| 61 | { \ | ||
| 62 | struct pci_dev *pci_dev; \ | ||
| 63 | struct platform_device *pdev; \ | ||
| 64 | struct proc_thermal_device *proc_dev; \ | ||
| 65 | \ | ||
| 66 | if (proc_thermal_emum_mode == PROC_THERMAL_PLATFORM_DEV) { \ | ||
| 67 | pdev = to_platform_device(dev); \ | ||
| 68 | proc_dev = platform_get_drvdata(pdev); \ | ||
| 69 | } else { \ | ||
| 70 | pci_dev = to_pci_dev(dev); \ | ||
| 71 | proc_dev = pci_get_drvdata(pci_dev); \ | ||
| 72 | } \ | ||
| 73 | return sprintf(buf, "%lu\n",\ | ||
| 74 | (unsigned long)proc_dev->power_limits[index].suffix * 1000); \ | ||
| 75 | } | ||
| 76 | |||
| 77 | POWER_LIMIT_SHOW(0, min_uw) | ||
| 78 | POWER_LIMIT_SHOW(0, max_uw) | ||
| 79 | POWER_LIMIT_SHOW(0, step_uw) | ||
| 80 | POWER_LIMIT_SHOW(0, tmin_us) | ||
| 81 | POWER_LIMIT_SHOW(0, tmax_us) | ||
| 82 | |||
| 83 | POWER_LIMIT_SHOW(1, min_uw) | ||
| 84 | POWER_LIMIT_SHOW(1, max_uw) | ||
| 85 | POWER_LIMIT_SHOW(1, step_uw) | ||
| 86 | POWER_LIMIT_SHOW(1, tmin_us) | ||
| 87 | POWER_LIMIT_SHOW(1, tmax_us) | ||
| 88 | |||
| 89 | static DEVICE_ATTR_RO(power_limit_0_min_uw); | ||
| 90 | static DEVICE_ATTR_RO(power_limit_0_max_uw); | ||
| 91 | static DEVICE_ATTR_RO(power_limit_0_step_uw); | ||
| 92 | static DEVICE_ATTR_RO(power_limit_0_tmin_us); | ||
| 93 | static DEVICE_ATTR_RO(power_limit_0_tmax_us); | ||
| 94 | |||
| 95 | static DEVICE_ATTR_RO(power_limit_1_min_uw); | ||
| 96 | static DEVICE_ATTR_RO(power_limit_1_max_uw); | ||
| 97 | static DEVICE_ATTR_RO(power_limit_1_step_uw); | ||
| 98 | static DEVICE_ATTR_RO(power_limit_1_tmin_us); | ||
| 99 | static DEVICE_ATTR_RO(power_limit_1_tmax_us); | ||
| 100 | |||
| 101 | static struct attribute *power_limit_attrs[] = { | ||
| 102 | &dev_attr_power_limit_0_min_uw.attr, | ||
| 103 | &dev_attr_power_limit_1_min_uw.attr, | ||
| 104 | &dev_attr_power_limit_0_max_uw.attr, | ||
| 105 | &dev_attr_power_limit_1_max_uw.attr, | ||
| 106 | &dev_attr_power_limit_0_step_uw.attr, | ||
| 107 | &dev_attr_power_limit_1_step_uw.attr, | ||
| 108 | &dev_attr_power_limit_0_tmin_us.attr, | ||
| 109 | &dev_attr_power_limit_1_tmin_us.attr, | ||
| 110 | &dev_attr_power_limit_0_tmax_us.attr, | ||
| 111 | &dev_attr_power_limit_1_tmax_us.attr, | ||
| 112 | NULL | ||
| 113 | }; | ||
| 114 | |||
| 115 | static struct attribute_group power_limit_attribute_group = { | ||
| 116 | .attrs = power_limit_attrs, | ||
| 117 | .name = "power_limits" | ||
| 118 | }; | ||
| 119 | |||
| 120 | static int proc_thermal_add(struct device *dev, | ||
| 121 | struct proc_thermal_device **priv) | ||
| 122 | { | ||
| 123 | struct proc_thermal_device *proc_priv; | ||
| 124 | struct acpi_device *adev; | ||
| 125 | acpi_status status; | ||
| 126 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 127 | union acpi_object *elements, *ppcc; | ||
| 128 | union acpi_object *p; | ||
| 129 | int i; | ||
| 130 | |||
| 131 | adev = ACPI_COMPANION(dev); | ||
| 132 | |||
| 133 | status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf); | ||
| 134 | if (ACPI_FAILURE(status)) | ||
| 135 | return -ENODEV; | ||
| 136 | |||
| 137 | p = buf.pointer; | ||
| 138 | if (!p || (p->type != ACPI_TYPE_PACKAGE)) { | ||
| 139 | dev_err(dev, "Invalid PPCC data\n"); | ||
| 140 | return -EFAULT; | ||
| 141 | } | ||
| 142 | if (!p->package.count) { | ||
| 143 | dev_err(dev, "Invalid PPCC package size\n"); | ||
| 144 | return -EFAULT; | ||
| 145 | } | ||
| 146 | |||
| 147 | proc_priv = devm_kzalloc(dev, sizeof(*proc_priv), GFP_KERNEL); | ||
| 148 | if (!proc_priv) | ||
| 149 | return -ENOMEM; | ||
| 150 | |||
| 151 | proc_priv->dev = dev; | ||
| 152 | proc_priv->adev = adev; | ||
| 153 | |||
| 154 | for (i = 0; i < min((int)p->package.count - 1, 2); ++i) { | ||
| 155 | elements = &(p->package.elements[i+1]); | ||
| 156 | if (elements->type != ACPI_TYPE_PACKAGE || | ||
| 157 | elements->package.count != 6) | ||
| 158 | return -EFAULT; | ||
| 159 | |||
| 160 | ppcc = elements->package.elements; | ||
| 161 | proc_priv->power_limits[i].index = ppcc[0].integer.value; | ||
| 162 | proc_priv->power_limits[i].min_uw = ppcc[1].integer.value; | ||
| 163 | proc_priv->power_limits[i].max_uw = ppcc[2].integer.value; | ||
| 164 | proc_priv->power_limits[i].tmin_us = ppcc[3].integer.value; | ||
| 165 | proc_priv->power_limits[i].tmax_us = ppcc[4].integer.value; | ||
| 166 | proc_priv->power_limits[i].step_uw = ppcc[5].integer.value; | ||
| 167 | } | ||
| 168 | |||
| 169 | *priv = proc_priv; | ||
| 170 | |||
