xref: /OK3568_Linux_fs/kernel/drivers/platform/x86/intel_menlow.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Intel menlow Driver for thermal management extension
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2008 Intel Corp
6*4882a593Smuzhiyun  *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
7*4882a593Smuzhiyun  *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  *  This driver creates the sys I/F for programming the sensors.
10*4882a593Smuzhiyun  *  It also implements the driver for intel menlow memory controller (hardware
11*4882a593Smuzhiyun  *  id is INT0002) which makes use of the platform specific ACPI methods
12*4882a593Smuzhiyun  *  to get/set bandwidth.
13*4882a593Smuzhiyun  */
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #include <linux/acpi.h>
18*4882a593Smuzhiyun #include <linux/kernel.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/pci.h>
21*4882a593Smuzhiyun #include <linux/pm.h>
22*4882a593Smuzhiyun #include <linux/slab.h>
23*4882a593Smuzhiyun #include <linux/thermal.h>
24*4882a593Smuzhiyun #include <linux/types.h>
25*4882a593Smuzhiyun #include <linux/units.h>
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun MODULE_AUTHOR("Thomas Sujith");
28*4882a593Smuzhiyun MODULE_AUTHOR("Zhang Rui");
29*4882a593Smuzhiyun MODULE_DESCRIPTION("Intel Menlow platform specific driver");
30*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun /*
33*4882a593Smuzhiyun  * Memory controller device control
34*4882a593Smuzhiyun  */
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun #define MEMORY_GET_BANDWIDTH "GTHS"
37*4882a593Smuzhiyun #define MEMORY_SET_BANDWIDTH "STHS"
38*4882a593Smuzhiyun #define MEMORY_ARG_CUR_BANDWIDTH 1
39*4882a593Smuzhiyun #define MEMORY_ARG_MAX_BANDWIDTH 0
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun static void intel_menlow_unregister_sensor(void);
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun /*
44*4882a593Smuzhiyun  * GTHS returning 'n' would mean that [0,n-1] states are supported
45*4882a593Smuzhiyun  * In that case max_cstate would be n-1
46*4882a593Smuzhiyun  * GTHS returning '0' would mean that no bandwidth control states are supported
47*4882a593Smuzhiyun  */
memory_get_max_bandwidth(struct thermal_cooling_device * cdev,unsigned long * max_state)48*4882a593Smuzhiyun static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
49*4882a593Smuzhiyun 				    unsigned long *max_state)
50*4882a593Smuzhiyun {
51*4882a593Smuzhiyun 	struct acpi_device *device = cdev->devdata;
52*4882a593Smuzhiyun 	acpi_handle handle = device->handle;
53*4882a593Smuzhiyun 	unsigned long long value;
54*4882a593Smuzhiyun 	struct acpi_object_list arg_list;
55*4882a593Smuzhiyun 	union acpi_object arg;
56*4882a593Smuzhiyun 	acpi_status status = AE_OK;
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	arg_list.count = 1;
59*4882a593Smuzhiyun 	arg_list.pointer = &arg;
60*4882a593Smuzhiyun 	arg.type = ACPI_TYPE_INTEGER;
61*4882a593Smuzhiyun 	arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
62*4882a593Smuzhiyun 	status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
63*4882a593Smuzhiyun 				       &arg_list, &value);
64*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
65*4882a593Smuzhiyun 		return -EFAULT;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	if (!value)
68*4882a593Smuzhiyun 		return -EINVAL;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	*max_state = value - 1;
71*4882a593Smuzhiyun 	return 0;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun 
memory_get_cur_bandwidth(struct thermal_cooling_device * cdev,unsigned long * value)74*4882a593Smuzhiyun static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
75*4882a593Smuzhiyun 				    unsigned long *value)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	struct acpi_device *device = cdev->devdata;
78*4882a593Smuzhiyun 	acpi_handle handle = device->handle;
79*4882a593Smuzhiyun 	unsigned long long result;
80*4882a593Smuzhiyun 	struct acpi_object_list arg_list;
81*4882a593Smuzhiyun 	union acpi_object arg;
82*4882a593Smuzhiyun 	acpi_status status = AE_OK;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	arg_list.count = 1;
85*4882a593Smuzhiyun 	arg_list.pointer = &arg;
86*4882a593Smuzhiyun 	arg.type = ACPI_TYPE_INTEGER;
87*4882a593Smuzhiyun 	arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
88*4882a593Smuzhiyun 	status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
89*4882a593Smuzhiyun 				       &arg_list, &result);
90*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
91*4882a593Smuzhiyun 		return -EFAULT;
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	*value = result;
94*4882a593Smuzhiyun 	return 0;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun 
memory_set_cur_bandwidth(struct thermal_cooling_device * cdev,unsigned long state)97*4882a593Smuzhiyun static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
98*4882a593Smuzhiyun 				    unsigned long state)
99*4882a593Smuzhiyun {
100*4882a593Smuzhiyun 	struct acpi_device *device = cdev->devdata;
101*4882a593Smuzhiyun 	acpi_handle handle = device->handle;
102*4882a593Smuzhiyun 	struct acpi_object_list arg_list;
103*4882a593Smuzhiyun 	union acpi_object arg;
104*4882a593Smuzhiyun 	acpi_status status;
105*4882a593Smuzhiyun 	unsigned long long temp;
106*4882a593Smuzhiyun 	unsigned long max_state;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	if (memory_get_max_bandwidth(cdev, &max_state))
109*4882a593Smuzhiyun 		return -EFAULT;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	if (state > max_state)
112*4882a593Smuzhiyun 		return -EINVAL;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	arg_list.count = 1;
115*4882a593Smuzhiyun 	arg_list.pointer = &arg;
116*4882a593Smuzhiyun 	arg.type = ACPI_TYPE_INTEGER;
117*4882a593Smuzhiyun 	arg.integer.value = state;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	status =
120*4882a593Smuzhiyun 	    acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
121*4882a593Smuzhiyun 				  &temp);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	pr_info("Bandwidth value was %ld: status is %d\n", state, status);
124*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
125*4882a593Smuzhiyun 		return -EFAULT;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun static const struct thermal_cooling_device_ops memory_cooling_ops = {
131*4882a593Smuzhiyun 	.get_max_state = memory_get_max_bandwidth,
132*4882a593Smuzhiyun 	.get_cur_state = memory_get_cur_bandwidth,
133*4882a593Smuzhiyun 	.set_cur_state = memory_set_cur_bandwidth,
134*4882a593Smuzhiyun };
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun /*
137*4882a593Smuzhiyun  * Memory Device Management
138*4882a593Smuzhiyun  */
intel_menlow_memory_add(struct acpi_device * device)139*4882a593Smuzhiyun static int intel_menlow_memory_add(struct acpi_device *device)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun 	int result = -ENODEV;
142*4882a593Smuzhiyun 	struct thermal_cooling_device *cdev;
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun 	if (!device)
145*4882a593Smuzhiyun 		return -EINVAL;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	if (!acpi_has_method(device->handle, MEMORY_GET_BANDWIDTH))
148*4882a593Smuzhiyun 		goto end;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	if (!acpi_has_method(device->handle, MEMORY_SET_BANDWIDTH))
151*4882a593Smuzhiyun 		goto end;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	cdev = thermal_cooling_device_register("Memory controller", device,
154*4882a593Smuzhiyun 					       &memory_cooling_ops);
155*4882a593Smuzhiyun 	if (IS_ERR(cdev)) {
156*4882a593Smuzhiyun 		result = PTR_ERR(cdev);
157*4882a593Smuzhiyun 		goto end;
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	device->driver_data = cdev;
161*4882a593Smuzhiyun 	result = sysfs_create_link(&device->dev.kobj,
162*4882a593Smuzhiyun 				&cdev->device.kobj, "thermal_cooling");
163*4882a593Smuzhiyun 	if (result)
164*4882a593Smuzhiyun 		goto unregister;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	result = sysfs_create_link(&cdev->device.kobj,
167*4882a593Smuzhiyun 				&device->dev.kobj, "device");
168*4882a593Smuzhiyun 	if (result) {
169*4882a593Smuzhiyun 		sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
170*4882a593Smuzhiyun 		goto unregister;
171*4882a593Smuzhiyun 	}
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun  end:
174*4882a593Smuzhiyun 	return result;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun  unregister:
177*4882a593Smuzhiyun 	thermal_cooling_device_unregister(cdev);
178*4882a593Smuzhiyun 	return result;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
intel_menlow_memory_remove(struct acpi_device * device)182*4882a593Smuzhiyun static int intel_menlow_memory_remove(struct acpi_device *device)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	struct thermal_cooling_device *cdev;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	if (!device)
187*4882a593Smuzhiyun 		return -EINVAL;
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	cdev = acpi_driver_data(device);
190*4882a593Smuzhiyun 	if (!cdev)
191*4882a593Smuzhiyun 		return -EINVAL;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
194*4882a593Smuzhiyun 	sysfs_remove_link(&cdev->device.kobj, "device");
195*4882a593Smuzhiyun 	thermal_cooling_device_unregister(cdev);
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	return 0;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun 
200*4882a593Smuzhiyun static const struct acpi_device_id intel_menlow_memory_ids[] = {
201*4882a593Smuzhiyun 	{"INT0002", 0},
202*4882a593Smuzhiyun 	{"", 0},
203*4882a593Smuzhiyun };
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun static struct acpi_driver intel_menlow_memory_driver = {
206*4882a593Smuzhiyun 	.name = "intel_menlow_thermal_control",
207*4882a593Smuzhiyun 	.ids = intel_menlow_memory_ids,
208*4882a593Smuzhiyun 	.ops = {
209*4882a593Smuzhiyun 		.add = intel_menlow_memory_add,
210*4882a593Smuzhiyun 		.remove = intel_menlow_memory_remove,
211*4882a593Smuzhiyun 		},
212*4882a593Smuzhiyun };
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun /*
215*4882a593Smuzhiyun  * Sensor control on menlow platform
216*4882a593Smuzhiyun  */
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun #define THERMAL_AUX0 0
219*4882a593Smuzhiyun #define THERMAL_AUX1 1
220*4882a593Smuzhiyun #define GET_AUX0 "GAX0"
221*4882a593Smuzhiyun #define GET_AUX1 "GAX1"
222*4882a593Smuzhiyun #define SET_AUX0 "SAX0"
223*4882a593Smuzhiyun #define SET_AUX1 "SAX1"
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun struct intel_menlow_attribute {
226*4882a593Smuzhiyun 	struct device_attribute attr;
227*4882a593Smuzhiyun 	struct device *device;
228*4882a593Smuzhiyun 	acpi_handle handle;
229*4882a593Smuzhiyun 	struct list_head node;
230*4882a593Smuzhiyun };
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun static LIST_HEAD(intel_menlow_attr_list);
233*4882a593Smuzhiyun static DEFINE_MUTEX(intel_menlow_attr_lock);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun /*
236*4882a593Smuzhiyun  * sensor_get_auxtrip - get the current auxtrip value from sensor
237*4882a593Smuzhiyun  * @name: Thermalzone name
238*4882a593Smuzhiyun  * @auxtype : AUX0/AUX1
239*4882a593Smuzhiyun  * @buf: syfs buffer
240*4882a593Smuzhiyun  */
sensor_get_auxtrip(acpi_handle handle,int index,unsigned long long * value)241*4882a593Smuzhiyun static int sensor_get_auxtrip(acpi_handle handle, int index,
242*4882a593Smuzhiyun 							unsigned long long *value)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun 	acpi_status status;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 	if ((index != 0 && index != 1) || !value)
247*4882a593Smuzhiyun 		return -EINVAL;
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
250*4882a593Smuzhiyun 				       NULL, value);
251*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
252*4882a593Smuzhiyun 		return -EIO;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	return 0;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun /*
258*4882a593Smuzhiyun  * sensor_set_auxtrip - set the new auxtrip value to sensor
259*4882a593Smuzhiyun  * @name: Thermalzone name
260*4882a593Smuzhiyun  * @auxtype : AUX0/AUX1
261*4882a593Smuzhiyun  * @buf: syfs buffer
262*4882a593Smuzhiyun  */
sensor_set_auxtrip(acpi_handle handle,int index,int value)263*4882a593Smuzhiyun static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun 	acpi_status status;
266*4882a593Smuzhiyun 	union acpi_object arg = {
267*4882a593Smuzhiyun 		ACPI_TYPE_INTEGER
268*4882a593Smuzhiyun 	};
269*4882a593Smuzhiyun 	struct acpi_object_list args = {
270*4882a593Smuzhiyun 		1, &arg
271*4882a593Smuzhiyun 	};
272*4882a593Smuzhiyun 	unsigned long long temp;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	if (index != 0 && index != 1)
275*4882a593Smuzhiyun 		return -EINVAL;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
278*4882a593Smuzhiyun 				       NULL, &temp);
279*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
280*4882a593Smuzhiyun 		return -EIO;
281*4882a593Smuzhiyun 	if ((index && value < temp) || (!index && value > temp))
282*4882a593Smuzhiyun 		return -EINVAL;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	arg.integer.value = value;
285*4882a593Smuzhiyun 	status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
286*4882a593Smuzhiyun 				       &args, &temp);
287*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
288*4882a593Smuzhiyun 		return -EIO;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	/* do we need to check the return value of SAX0/SAX1 ? */
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	return 0;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun #define to_intel_menlow_attr(_attr)	\
296*4882a593Smuzhiyun 	container_of(_attr, struct intel_menlow_attribute, attr)
297*4882a593Smuzhiyun 
aux_show(struct device * dev,struct device_attribute * dev_attr,char * buf,int idx)298*4882a593Smuzhiyun static ssize_t aux_show(struct device *dev, struct device_attribute *dev_attr,
299*4882a593Smuzhiyun 			char *buf, int idx)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun 	struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
302*4882a593Smuzhiyun 	unsigned long long value;
303*4882a593Smuzhiyun 	int result;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	result = sensor_get_auxtrip(attr->handle, idx, &value);
306*4882a593Smuzhiyun 	if (result)
307*4882a593Smuzhiyun 		return result;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	return sprintf(buf, "%lu", deci_kelvin_to_celsius(value));
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun 
aux0_show(struct device * dev,struct device_attribute * dev_attr,char * buf)312*4882a593Smuzhiyun static ssize_t aux0_show(struct device *dev,
313*4882a593Smuzhiyun 			 struct device_attribute *dev_attr, char *buf)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun 	return aux_show(dev, dev_attr, buf, 0);
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun 
aux1_show(struct device * dev,struct device_attribute * dev_attr,char * buf)318*4882a593Smuzhiyun static ssize_t aux1_show(struct device *dev,
319*4882a593Smuzhiyun 			 struct device_attribute *dev_attr, char *buf)
320*4882a593Smuzhiyun {
321*4882a593Smuzhiyun 	return aux_show(dev, dev_attr, buf, 1);
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun 
aux_store(struct device * dev,struct device_attribute * dev_attr,const char * buf,size_t count,int idx)324*4882a593Smuzhiyun static ssize_t aux_store(struct device *dev, struct device_attribute *dev_attr,
325*4882a593Smuzhiyun 			 const char *buf, size_t count, int idx)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
328*4882a593Smuzhiyun 	int value;
329*4882a593Smuzhiyun 	int result;
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	/*Sanity check; should be a positive integer */
332*4882a593Smuzhiyun 	if (!sscanf(buf, "%d", &value))
333*4882a593Smuzhiyun 		return -EINVAL;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	if (value < 0)
336*4882a593Smuzhiyun 		return -EINVAL;
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	result = sensor_set_auxtrip(attr->handle, idx,
339*4882a593Smuzhiyun 				    celsius_to_deci_kelvin(value));
340*4882a593Smuzhiyun 	return result ? result : count;
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun 
aux0_store(struct device * dev,struct device_attribute * dev_attr,const char * buf,size_t count)343*4882a593Smuzhiyun static ssize_t aux0_store(struct device *dev,
344*4882a593Smuzhiyun 			  struct device_attribute *dev_attr,
345*4882a593Smuzhiyun 			  const char *buf, size_t count)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun 	return aux_store(dev, dev_attr, buf, count, 0);
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun 
aux1_store(struct device * dev,struct device_attribute * dev_attr,const char * buf,size_t count)350*4882a593Smuzhiyun static ssize_t aux1_store(struct device *dev,
351*4882a593Smuzhiyun 			  struct device_attribute *dev_attr,
352*4882a593Smuzhiyun 			  const char *buf, size_t count)
353*4882a593Smuzhiyun {
354*4882a593Smuzhiyun 	return aux_store(dev, dev_attr, buf, count, 1);
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun /* BIOS can enable/disable the thermal user application in dabney platform */
358*4882a593Smuzhiyun #define BIOS_ENABLED "\\_TZ.GSTS"
bios_enabled_show(struct device * dev,struct device_attribute * attr,char * buf)359*4882a593Smuzhiyun static ssize_t bios_enabled_show(struct device *dev,
360*4882a593Smuzhiyun 				 struct device_attribute *attr, char *buf)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun 	acpi_status status;
363*4882a593Smuzhiyun 	unsigned long long bios_enabled;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
366*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
367*4882a593Smuzhiyun 		return -ENODEV;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun 
intel_menlow_add_one_attribute(char * name,umode_t mode,void * show,void * store,struct device * dev,acpi_handle handle)372*4882a593Smuzhiyun static int intel_menlow_add_one_attribute(char *name, umode_t mode, void *show,
373*4882a593Smuzhiyun 					  void *store, struct device *dev,
374*4882a593Smuzhiyun 					  acpi_handle handle)
375*4882a593Smuzhiyun {
376*4882a593Smuzhiyun 	struct intel_menlow_attribute *attr;
377*4882a593Smuzhiyun 	int result;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
380*4882a593Smuzhiyun 	if (!attr)
381*4882a593Smuzhiyun 		return -ENOMEM;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	sysfs_attr_init(&attr->attr.attr); /* That is consistent naming :D */
384*4882a593Smuzhiyun 	attr->attr.attr.name = name;
385*4882a593Smuzhiyun 	attr->attr.attr.mode = mode;
386*4882a593Smuzhiyun 	attr->attr.show = show;
387*4882a593Smuzhiyun 	attr->attr.store = store;
388*4882a593Smuzhiyun 	attr->device = dev;
389*4882a593Smuzhiyun 	attr->handle = handle;
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun 	result = device_create_file(dev, &attr->attr);
392*4882a593Smuzhiyun 	if (result) {
393*4882a593Smuzhiyun 		kfree(attr);
394*4882a593Smuzhiyun 		return result;
395*4882a593Smuzhiyun 	}
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	mutex_lock(&intel_menlow_attr_lock);
398*4882a593Smuzhiyun 	list_add_tail(&attr->node, &intel_menlow_attr_list);
399*4882a593Smuzhiyun 	mutex_unlock(&intel_menlow_attr_lock);
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	return 0;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun 
intel_menlow_register_sensor(acpi_handle handle,u32 lvl,void * context,void ** rv)404*4882a593Smuzhiyun static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
405*4882a593Smuzhiyun 						void *context, void **rv)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun 	acpi_status status;
408*4882a593Smuzhiyun 	acpi_handle dummy;
409*4882a593Smuzhiyun 	struct thermal_zone_device *thermal;
410*4882a593Smuzhiyun 	int result;
411*4882a593Smuzhiyun 
412*4882a593Smuzhiyun 	result = acpi_bus_get_private_data(handle, (void **)&thermal);
413*4882a593Smuzhiyun 	if (result)
414*4882a593Smuzhiyun 		return 0;
415*4882a593Smuzhiyun 
416*4882a593Smuzhiyun 	/* _TZ must have the AUX0/1 methods */
417*4882a593Smuzhiyun 	status = acpi_get_handle(handle, GET_AUX0, &dummy);
418*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
419*4882a593Smuzhiyun 		return (status == AE_NOT_FOUND) ? AE_OK : status;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	status = acpi_get_handle(handle, SET_AUX0, &dummy);
422*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
423*4882a593Smuzhiyun 		return (status == AE_NOT_FOUND) ? AE_OK : status;
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 	result = intel_menlow_add_one_attribute("aux0", 0644,
426*4882a593Smuzhiyun 						aux0_show, aux0_store,
427*4882a593Smuzhiyun 						&thermal->device, handle);
428*4882a593Smuzhiyun 	if (result)
429*4882a593Smuzhiyun 		return AE_ERROR;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	status = acpi_get_handle(handle, GET_AUX1, &dummy);
432*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
433*4882a593Smuzhiyun 		goto aux1_not_found;
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	status = acpi_get_handle(handle, SET_AUX1, &dummy);
436*4882a593Smuzhiyun 	if (ACPI_FAILURE(status))
437*4882a593Smuzhiyun 		goto aux1_not_found;
438*4882a593Smuzhiyun 
439*4882a593Smuzhiyun 	result = intel_menlow_add_one_attribute("aux1", 0644,
440*4882a593Smuzhiyun 						aux1_show, aux1_store,
441*4882a593Smuzhiyun 						&thermal->device, handle);
442*4882a593Smuzhiyun 	if (result) {
443*4882a593Smuzhiyun 		intel_menlow_unregister_sensor();
444*4882a593Smuzhiyun 		return AE_ERROR;
445*4882a593Smuzhiyun 	}
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	/*
448*4882a593Smuzhiyun 	 * create the "dabney_enabled" attribute which means the user app
449*4882a593Smuzhiyun 	 * should be loaded or not
450*4882a593Smuzhiyun 	 */
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	result = intel_menlow_add_one_attribute("bios_enabled", 0444,
453*4882a593Smuzhiyun 						bios_enabled_show, NULL,
454*4882a593Smuzhiyun 						&thermal->device, handle);
455*4882a593Smuzhiyun 	if (result) {
456*4882a593Smuzhiyun 		intel_menlow_unregister_sensor();
457*4882a593Smuzhiyun 		return AE_ERROR;
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	return AE_OK;
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun  aux1_not_found:
463*4882a593Smuzhiyun 	if (status == AE_NOT_FOUND)
464*4882a593Smuzhiyun 		return AE_OK;
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	intel_menlow_unregister_sensor();
467*4882a593Smuzhiyun 	return status;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun 
intel_menlow_unregister_sensor(void)470*4882a593Smuzhiyun static void intel_menlow_unregister_sensor(void)
471*4882a593Smuzhiyun {
472*4882a593Smuzhiyun 	struct intel_menlow_attribute *pos, *next;
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	mutex_lock(&intel_menlow_attr_lock);
475*4882a593Smuzhiyun 	list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
476*4882a593Smuzhiyun 		list_del(&pos->node);
477*4882a593Smuzhiyun 		device_remove_file(pos->device, &pos->attr);
478*4882a593Smuzhiyun 		kfree(pos);
479*4882a593Smuzhiyun 	}
480*4882a593Smuzhiyun 	mutex_unlock(&intel_menlow_attr_lock);
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	return;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun 
intel_menlow_module_init(void)485*4882a593Smuzhiyun static int __init intel_menlow_module_init(void)
486*4882a593Smuzhiyun {
487*4882a593Smuzhiyun 	int result = -ENODEV;
488*4882a593Smuzhiyun 	acpi_status status;
489*4882a593Smuzhiyun 	unsigned long long enable;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	if (acpi_disabled)
492*4882a593Smuzhiyun 		return result;
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	/* Looking for the \_TZ.GSTS method */
495*4882a593Smuzhiyun 	status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
496*4882a593Smuzhiyun 	if (ACPI_FAILURE(status) || !enable)
497*4882a593Smuzhiyun 		return -ENODEV;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	/* Looking for ACPI device MEM0 with hardware id INT0002 */
500*4882a593Smuzhiyun 	result = acpi_bus_register_driver(&intel_menlow_memory_driver);
501*4882a593Smuzhiyun 	if (result)
502*4882a593Smuzhiyun 		return result;
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun 	/* Looking for sensors in each ACPI thermal zone */
505*4882a593Smuzhiyun 	status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
506*4882a593Smuzhiyun 				     ACPI_UINT32_MAX,
507*4882a593Smuzhiyun 				     intel_menlow_register_sensor, NULL, NULL, NULL);
508*4882a593Smuzhiyun 	if (ACPI_FAILURE(status)) {
509*4882a593Smuzhiyun 		acpi_bus_unregister_driver(&intel_menlow_memory_driver);
510*4882a593Smuzhiyun 		return -ENODEV;
511*4882a593Smuzhiyun 	}
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	return 0;
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun 
intel_menlow_module_exit(void)516*4882a593Smuzhiyun static void __exit intel_menlow_module_exit(void)
517*4882a593Smuzhiyun {
518*4882a593Smuzhiyun 	acpi_bus_unregister_driver(&intel_menlow_memory_driver);
519*4882a593Smuzhiyun 	intel_menlow_unregister_sensor();
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun module_init(intel_menlow_module_init);
523*4882a593Smuzhiyun module_exit(intel_menlow_module_exit);
524