18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Intel menlow Driver for thermal management extension
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Intel Corp
68c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
78c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  This driver creates the sys I/F for programming the sensors.
108c2ecf20Sopenharmony_ci *  It also implements the driver for intel menlow memory controller (hardware
118c2ecf20Sopenharmony_ci *  id is INT0002) which makes use of the platform specific ACPI methods
128c2ecf20Sopenharmony_ci *  to get/set bandwidth.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/acpi.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/pci.h>
218c2ecf20Sopenharmony_ci#include <linux/pm.h>
228c2ecf20Sopenharmony_ci#include <linux/slab.h>
238c2ecf20Sopenharmony_ci#include <linux/thermal.h>
248c2ecf20Sopenharmony_ci#include <linux/types.h>
258c2ecf20Sopenharmony_ci#include <linux/units.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thomas Sujith");
288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zhang Rui");
298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel Menlow platform specific driver");
308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * Memory controller device control
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define MEMORY_GET_BANDWIDTH "GTHS"
378c2ecf20Sopenharmony_ci#define MEMORY_SET_BANDWIDTH "STHS"
388c2ecf20Sopenharmony_ci#define MEMORY_ARG_CUR_BANDWIDTH 1
398c2ecf20Sopenharmony_ci#define MEMORY_ARG_MAX_BANDWIDTH 0
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void intel_menlow_unregister_sensor(void);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/*
448c2ecf20Sopenharmony_ci * GTHS returning 'n' would mean that [0,n-1] states are supported
458c2ecf20Sopenharmony_ci * In that case max_cstate would be n-1
468c2ecf20Sopenharmony_ci * GTHS returning '0' would mean that no bandwidth control states are supported
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistatic int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
498c2ecf20Sopenharmony_ci				    unsigned long *max_state)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct acpi_device *device = cdev->devdata;
528c2ecf20Sopenharmony_ci	acpi_handle handle = device->handle;
538c2ecf20Sopenharmony_ci	unsigned long long value;
548c2ecf20Sopenharmony_ci	struct acpi_object_list arg_list;
558c2ecf20Sopenharmony_ci	union acpi_object arg;
568c2ecf20Sopenharmony_ci	acpi_status status = AE_OK;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	arg_list.count = 1;
598c2ecf20Sopenharmony_ci	arg_list.pointer = &arg;
608c2ecf20Sopenharmony_ci	arg.type = ACPI_TYPE_INTEGER;
618c2ecf20Sopenharmony_ci	arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
628c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
638c2ecf20Sopenharmony_ci				       &arg_list, &value);
648c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
658c2ecf20Sopenharmony_ci		return -EFAULT;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (!value)
688c2ecf20Sopenharmony_ci		return -EINVAL;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	*max_state = value - 1;
718c2ecf20Sopenharmony_ci	return 0;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
758c2ecf20Sopenharmony_ci				    unsigned long *value)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	struct acpi_device *device = cdev->devdata;
788c2ecf20Sopenharmony_ci	acpi_handle handle = device->handle;
798c2ecf20Sopenharmony_ci	unsigned long long result;
808c2ecf20Sopenharmony_ci	struct acpi_object_list arg_list;
818c2ecf20Sopenharmony_ci	union acpi_object arg;
828c2ecf20Sopenharmony_ci	acpi_status status = AE_OK;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	arg_list.count = 1;
858c2ecf20Sopenharmony_ci	arg_list.pointer = &arg;
868c2ecf20Sopenharmony_ci	arg.type = ACPI_TYPE_INTEGER;
878c2ecf20Sopenharmony_ci	arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
888c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
898c2ecf20Sopenharmony_ci				       &arg_list, &result);
908c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
918c2ecf20Sopenharmony_ci		return -EFAULT;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	*value = result;
948c2ecf20Sopenharmony_ci	return 0;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
988c2ecf20Sopenharmony_ci				    unsigned long state)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	struct acpi_device *device = cdev->devdata;
1018c2ecf20Sopenharmony_ci	acpi_handle handle = device->handle;
1028c2ecf20Sopenharmony_ci	struct acpi_object_list arg_list;
1038c2ecf20Sopenharmony_ci	union acpi_object arg;
1048c2ecf20Sopenharmony_ci	acpi_status status;
1058c2ecf20Sopenharmony_ci	unsigned long long temp;
1068c2ecf20Sopenharmony_ci	unsigned long max_state;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (memory_get_max_bandwidth(cdev, &max_state))
1098c2ecf20Sopenharmony_ci		return -EFAULT;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (state > max_state)
1128c2ecf20Sopenharmony_ci		return -EINVAL;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	arg_list.count = 1;
1158c2ecf20Sopenharmony_ci	arg_list.pointer = &arg;
1168c2ecf20Sopenharmony_ci	arg.type = ACPI_TYPE_INTEGER;
1178c2ecf20Sopenharmony_ci	arg.integer.value = state;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	status =
1208c2ecf20Sopenharmony_ci	    acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
1218c2ecf20Sopenharmony_ci				  &temp);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	pr_info("Bandwidth value was %ld: status is %d\n", state, status);
1248c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
1258c2ecf20Sopenharmony_ci		return -EFAULT;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic const struct thermal_cooling_device_ops memory_cooling_ops = {
1318c2ecf20Sopenharmony_ci	.get_max_state = memory_get_max_bandwidth,
1328c2ecf20Sopenharmony_ci	.get_cur_state = memory_get_cur_bandwidth,
1338c2ecf20Sopenharmony_ci	.set_cur_state = memory_set_cur_bandwidth,
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci/*
1378c2ecf20Sopenharmony_ci * Memory Device Management
1388c2ecf20Sopenharmony_ci */
1398c2ecf20Sopenharmony_cistatic int intel_menlow_memory_add(struct acpi_device *device)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	int result = -ENODEV;
1428c2ecf20Sopenharmony_ci	struct thermal_cooling_device *cdev;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!device)
1458c2ecf20Sopenharmony_ci		return -EINVAL;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!acpi_has_method(device->handle, MEMORY_GET_BANDWIDTH))
1488c2ecf20Sopenharmony_ci		goto end;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (!acpi_has_method(device->handle, MEMORY_SET_BANDWIDTH))
1518c2ecf20Sopenharmony_ci		goto end;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	cdev = thermal_cooling_device_register("Memory controller", device,
1548c2ecf20Sopenharmony_ci					       &memory_cooling_ops);
1558c2ecf20Sopenharmony_ci	if (IS_ERR(cdev)) {
1568c2ecf20Sopenharmony_ci		result = PTR_ERR(cdev);
1578c2ecf20Sopenharmony_ci		goto end;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	device->driver_data = cdev;
1618c2ecf20Sopenharmony_ci	result = sysfs_create_link(&device->dev.kobj,
1628c2ecf20Sopenharmony_ci				&cdev->device.kobj, "thermal_cooling");
1638c2ecf20Sopenharmony_ci	if (result)
1648c2ecf20Sopenharmony_ci		goto unregister;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	result = sysfs_create_link(&cdev->device.kobj,
1678c2ecf20Sopenharmony_ci				&device->dev.kobj, "device");
1688c2ecf20Sopenharmony_ci	if (result) {
1698c2ecf20Sopenharmony_ci		sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
1708c2ecf20Sopenharmony_ci		goto unregister;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci end:
1748c2ecf20Sopenharmony_ci	return result;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci unregister:
1778c2ecf20Sopenharmony_ci	thermal_cooling_device_unregister(cdev);
1788c2ecf20Sopenharmony_ci	return result;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic int intel_menlow_memory_remove(struct acpi_device *device)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct thermal_cooling_device *cdev;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (!device)
1878c2ecf20Sopenharmony_ci		return -EINVAL;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	cdev = acpi_driver_data(device);
1908c2ecf20Sopenharmony_ci	if (!cdev)
1918c2ecf20Sopenharmony_ci		return -EINVAL;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
1948c2ecf20Sopenharmony_ci	sysfs_remove_link(&cdev->device.kobj, "device");
1958c2ecf20Sopenharmony_ci	thermal_cooling_device_unregister(cdev);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return 0;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic const struct acpi_device_id intel_menlow_memory_ids[] = {
2018c2ecf20Sopenharmony_ci	{"INT0002", 0},
2028c2ecf20Sopenharmony_ci	{"", 0},
2038c2ecf20Sopenharmony_ci};
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic struct acpi_driver intel_menlow_memory_driver = {
2068c2ecf20Sopenharmony_ci	.name = "intel_menlow_thermal_control",
2078c2ecf20Sopenharmony_ci	.ids = intel_menlow_memory_ids,
2088c2ecf20Sopenharmony_ci	.ops = {
2098c2ecf20Sopenharmony_ci		.add = intel_menlow_memory_add,
2108c2ecf20Sopenharmony_ci		.remove = intel_menlow_memory_remove,
2118c2ecf20Sopenharmony_ci		},
2128c2ecf20Sopenharmony_ci};
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/*
2158c2ecf20Sopenharmony_ci * Sensor control on menlow platform
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci#define THERMAL_AUX0 0
2198c2ecf20Sopenharmony_ci#define THERMAL_AUX1 1
2208c2ecf20Sopenharmony_ci#define GET_AUX0 "GAX0"
2218c2ecf20Sopenharmony_ci#define GET_AUX1 "GAX1"
2228c2ecf20Sopenharmony_ci#define SET_AUX0 "SAX0"
2238c2ecf20Sopenharmony_ci#define SET_AUX1 "SAX1"
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistruct intel_menlow_attribute {
2268c2ecf20Sopenharmony_ci	struct device_attribute attr;
2278c2ecf20Sopenharmony_ci	struct device *device;
2288c2ecf20Sopenharmony_ci	acpi_handle handle;
2298c2ecf20Sopenharmony_ci	struct list_head node;
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic LIST_HEAD(intel_menlow_attr_list);
2338c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(intel_menlow_attr_lock);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci/*
2368c2ecf20Sopenharmony_ci * sensor_get_auxtrip - get the current auxtrip value from sensor
2378c2ecf20Sopenharmony_ci * @name: Thermalzone name
2388c2ecf20Sopenharmony_ci * @auxtype : AUX0/AUX1
2398c2ecf20Sopenharmony_ci * @buf: syfs buffer
2408c2ecf20Sopenharmony_ci */
2418c2ecf20Sopenharmony_cistatic int sensor_get_auxtrip(acpi_handle handle, int index,
2428c2ecf20Sopenharmony_ci							unsigned long long *value)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	acpi_status status;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if ((index != 0 && index != 1) || !value)
2478c2ecf20Sopenharmony_ci		return -EINVAL;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
2508c2ecf20Sopenharmony_ci				       NULL, value);
2518c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
2528c2ecf20Sopenharmony_ci		return -EIO;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci/*
2588c2ecf20Sopenharmony_ci * sensor_set_auxtrip - set the new auxtrip value to sensor
2598c2ecf20Sopenharmony_ci * @name: Thermalzone name
2608c2ecf20Sopenharmony_ci * @auxtype : AUX0/AUX1
2618c2ecf20Sopenharmony_ci * @buf: syfs buffer
2628c2ecf20Sopenharmony_ci */
2638c2ecf20Sopenharmony_cistatic int sensor_set_auxtrip(acpi_handle handle, int index, int value)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	acpi_status status;
2668c2ecf20Sopenharmony_ci	union acpi_object arg = {
2678c2ecf20Sopenharmony_ci		ACPI_TYPE_INTEGER
2688c2ecf20Sopenharmony_ci	};
2698c2ecf20Sopenharmony_ci	struct acpi_object_list args = {
2708c2ecf20Sopenharmony_ci		1, &arg
2718c2ecf20Sopenharmony_ci	};
2728c2ecf20Sopenharmony_ci	unsigned long long temp;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (index != 0 && index != 1)
2758c2ecf20Sopenharmony_ci		return -EINVAL;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
2788c2ecf20Sopenharmony_ci				       NULL, &temp);
2798c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
2808c2ecf20Sopenharmony_ci		return -EIO;
2818c2ecf20Sopenharmony_ci	if ((index && value < temp) || (!index && value > temp))
2828c2ecf20Sopenharmony_ci		return -EINVAL;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	arg.integer.value = value;
2858c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
2868c2ecf20Sopenharmony_ci				       &args, &temp);
2878c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
2888c2ecf20Sopenharmony_ci		return -EIO;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	/* do we need to check the return value of SAX0/SAX1 ? */
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return 0;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci#define to_intel_menlow_attr(_attr)	\
2968c2ecf20Sopenharmony_ci	container_of(_attr, struct intel_menlow_attribute, attr)
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic ssize_t aux_show(struct device *dev, struct device_attribute *dev_attr,
2998c2ecf20Sopenharmony_ci			char *buf, int idx)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
3028c2ecf20Sopenharmony_ci	unsigned long long value;
3038c2ecf20Sopenharmony_ci	int result;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	result = sensor_get_auxtrip(attr->handle, idx, &value);
3068c2ecf20Sopenharmony_ci	if (result)
3078c2ecf20Sopenharmony_ci		return result;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return sprintf(buf, "%lu", deci_kelvin_to_celsius(value));
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic ssize_t aux0_show(struct device *dev,
3138c2ecf20Sopenharmony_ci			 struct device_attribute *dev_attr, char *buf)
3148c2ecf20Sopenharmony_ci{
3158c2ecf20Sopenharmony_ci	return aux_show(dev, dev_attr, buf, 0);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic ssize_t aux1_show(struct device *dev,
3198c2ecf20Sopenharmony_ci			 struct device_attribute *dev_attr, char *buf)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	return aux_show(dev, dev_attr, buf, 1);
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic ssize_t aux_store(struct device *dev, struct device_attribute *dev_attr,
3258c2ecf20Sopenharmony_ci			 const char *buf, size_t count, int idx)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
3288c2ecf20Sopenharmony_ci	int value;
3298c2ecf20Sopenharmony_ci	int result;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	/*Sanity check; should be a positive integer */
3328c2ecf20Sopenharmony_ci	if (!sscanf(buf, "%d", &value))
3338c2ecf20Sopenharmony_ci		return -EINVAL;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (value < 0)
3368c2ecf20Sopenharmony_ci		return -EINVAL;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	result = sensor_set_auxtrip(attr->handle, idx,
3398c2ecf20Sopenharmony_ci				    celsius_to_deci_kelvin(value));
3408c2ecf20Sopenharmony_ci	return result ? result : count;
3418c2ecf20Sopenharmony_ci}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic ssize_t aux0_store(struct device *dev,
3448c2ecf20Sopenharmony_ci			  struct device_attribute *dev_attr,
3458c2ecf20Sopenharmony_ci			  const char *buf, size_t count)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	return aux_store(dev, dev_attr, buf, count, 0);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic ssize_t aux1_store(struct device *dev,
3518c2ecf20Sopenharmony_ci			  struct device_attribute *dev_attr,
3528c2ecf20Sopenharmony_ci			  const char *buf, size_t count)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	return aux_store(dev, dev_attr, buf, count, 1);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci/* BIOS can enable/disable the thermal user application in dabney platform */
3588c2ecf20Sopenharmony_ci#define BIOS_ENABLED "\\_TZ.GSTS"
3598c2ecf20Sopenharmony_cistatic ssize_t bios_enabled_show(struct device *dev,
3608c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	acpi_status status;
3638c2ecf20Sopenharmony_ci	unsigned long long bios_enabled;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
3668c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
3678c2ecf20Sopenharmony_ci		return -ENODEV;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int intel_menlow_add_one_attribute(char *name, umode_t mode, void *show,
3738c2ecf20Sopenharmony_ci					  void *store, struct device *dev,
3748c2ecf20Sopenharmony_ci					  acpi_handle handle)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct intel_menlow_attribute *attr;
3778c2ecf20Sopenharmony_ci	int result;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
3808c2ecf20Sopenharmony_ci	if (!attr)
3818c2ecf20Sopenharmony_ci		return -ENOMEM;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	sysfs_attr_init(&attr->attr.attr); /* That is consistent naming :D */
3848c2ecf20Sopenharmony_ci	attr->attr.attr.name = name;
3858c2ecf20Sopenharmony_ci	attr->attr.attr.mode = mode;
3868c2ecf20Sopenharmony_ci	attr->attr.show = show;
3878c2ecf20Sopenharmony_ci	attr->attr.store = store;
3888c2ecf20Sopenharmony_ci	attr->device = dev;
3898c2ecf20Sopenharmony_ci	attr->handle = handle;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	result = device_create_file(dev, &attr->attr);
3928c2ecf20Sopenharmony_ci	if (result) {
3938c2ecf20Sopenharmony_ci		kfree(attr);
3948c2ecf20Sopenharmony_ci		return result;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	mutex_lock(&intel_menlow_attr_lock);
3988c2ecf20Sopenharmony_ci	list_add_tail(&attr->node, &intel_menlow_attr_list);
3998c2ecf20Sopenharmony_ci	mutex_unlock(&intel_menlow_attr_lock);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return 0;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
4058c2ecf20Sopenharmony_ci						void *context, void **rv)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	acpi_status status;
4088c2ecf20Sopenharmony_ci	acpi_handle dummy;
4098c2ecf20Sopenharmony_ci	struct thermal_zone_device *thermal;
4108c2ecf20Sopenharmony_ci	int result;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	result = acpi_bus_get_private_data(handle, (void **)&thermal);
4138c2ecf20Sopenharmony_ci	if (result)
4148c2ecf20Sopenharmony_ci		return 0;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/* _TZ must have the AUX0/1 methods */
4178c2ecf20Sopenharmony_ci	status = acpi_get_handle(handle, GET_AUX0, &dummy);
4188c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
4198c2ecf20Sopenharmony_ci		return (status == AE_NOT_FOUND) ? AE_OK : status;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	status = acpi_get_handle(handle, SET_AUX0, &dummy);
4228c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
4238c2ecf20Sopenharmony_ci		return (status == AE_NOT_FOUND) ? AE_OK : status;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	result = intel_menlow_add_one_attribute("aux0", 0644,
4268c2ecf20Sopenharmony_ci						aux0_show, aux0_store,
4278c2ecf20Sopenharmony_ci						&thermal->device, handle);
4288c2ecf20Sopenharmony_ci	if (result)
4298c2ecf20Sopenharmony_ci		return AE_ERROR;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	status = acpi_get_handle(handle, GET_AUX1, &dummy);
4328c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
4338c2ecf20Sopenharmony_ci		goto aux1_not_found;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	status = acpi_get_handle(handle, SET_AUX1, &dummy);
4368c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
4378c2ecf20Sopenharmony_ci		goto aux1_not_found;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	result = intel_menlow_add_one_attribute("aux1", 0644,
4408c2ecf20Sopenharmony_ci						aux1_show, aux1_store,
4418c2ecf20Sopenharmony_ci						&thermal->device, handle);
4428c2ecf20Sopenharmony_ci	if (result) {
4438c2ecf20Sopenharmony_ci		intel_menlow_unregister_sensor();
4448c2ecf20Sopenharmony_ci		return AE_ERROR;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/*
4488c2ecf20Sopenharmony_ci	 * create the "dabney_enabled" attribute which means the user app
4498c2ecf20Sopenharmony_ci	 * should be loaded or not
4508c2ecf20Sopenharmony_ci	 */
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	result = intel_menlow_add_one_attribute("bios_enabled", 0444,
4538c2ecf20Sopenharmony_ci						bios_enabled_show, NULL,
4548c2ecf20Sopenharmony_ci						&thermal->device, handle);
4558c2ecf20Sopenharmony_ci	if (result) {
4568c2ecf20Sopenharmony_ci		intel_menlow_unregister_sensor();
4578c2ecf20Sopenharmony_ci		return AE_ERROR;
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return AE_OK;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci aux1_not_found:
4638c2ecf20Sopenharmony_ci	if (status == AE_NOT_FOUND)
4648c2ecf20Sopenharmony_ci		return AE_OK;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	intel_menlow_unregister_sensor();
4678c2ecf20Sopenharmony_ci	return status;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic void intel_menlow_unregister_sensor(void)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	struct intel_menlow_attribute *pos, *next;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	mutex_lock(&intel_menlow_attr_lock);
4758c2ecf20Sopenharmony_ci	list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
4768c2ecf20Sopenharmony_ci		list_del(&pos->node);
4778c2ecf20Sopenharmony_ci		device_remove_file(pos->device, &pos->attr);
4788c2ecf20Sopenharmony_ci		kfree(pos);
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	mutex_unlock(&intel_menlow_attr_lock);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	return;
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic int __init intel_menlow_module_init(void)
4868c2ecf20Sopenharmony_ci{
4878c2ecf20Sopenharmony_ci	int result = -ENODEV;
4888c2ecf20Sopenharmony_ci	acpi_status status;
4898c2ecf20Sopenharmony_ci	unsigned long long enable;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	if (acpi_disabled)
4928c2ecf20Sopenharmony_ci		return result;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* Looking for the \_TZ.GSTS method */
4958c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
4968c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status) || !enable)
4978c2ecf20Sopenharmony_ci		return -ENODEV;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	/* Looking for ACPI device MEM0 with hardware id INT0002 */
5008c2ecf20Sopenharmony_ci	result = acpi_bus_register_driver(&intel_menlow_memory_driver);
5018c2ecf20Sopenharmony_ci	if (result)
5028c2ecf20Sopenharmony_ci		return result;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	/* Looking for sensors in each ACPI thermal zone */
5058c2ecf20Sopenharmony_ci	status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
5068c2ecf20Sopenharmony_ci				     ACPI_UINT32_MAX,
5078c2ecf20Sopenharmony_ci				     intel_menlow_register_sensor, NULL, NULL, NULL);
5088c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status)) {
5098c2ecf20Sopenharmony_ci		acpi_bus_unregister_driver(&intel_menlow_memory_driver);
5108c2ecf20Sopenharmony_ci		return -ENODEV;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	return 0;
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic void __exit intel_menlow_module_exit(void)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	acpi_bus_unregister_driver(&intel_menlow_memory_driver);
5198c2ecf20Sopenharmony_ci	intel_menlow_unregister_sensor();
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cimodule_init(intel_menlow_module_init);
5238c2ecf20Sopenharmony_cimodule_exit(intel_menlow_module_exit);
524