aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorDurgadoss R <durgadoss.r@intel.com>2011-02-20 12:35:43 -0500
committerMatthew Garrett <mjg@redhat.com>2011-03-28 06:07:19 -0400
commitf017fbe7993d8416824aaf079fa1e6dc88d1ba55 (patch)
tree598b1f6a1d13a14eeb1b31325037a78e75f2aa8b /drivers/platform
parentc0b9c6494498014f28dbec37fe327bf016a91356 (diff)
medfield: Add Thermal Driver
This is the basic thermal sensor driver for Intel MID platform using the Medfield chipset. It plugs in via the thermal drivers and provides sensor readings for the device sensors. Signed-off-by: Durgadoss R <durgadoss.r@intel.com> Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig7
-rw-r--r--drivers/platform/x86/Makefile3
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c575
3 files changed, 584 insertions, 1 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 53f818d23edb..b039d6a93f17 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -637,6 +637,13 @@ config INTEL_MID_POWER_BUTTON
637 637
638 If unsure, say N. 638 If unsure, say N.
639 639
640config INTEL_MFLD_THERMAL
641 tristate "Thermal driver for Intel Medfield platform"
642 depends on INTEL_SCU_IPC && THERMAL
643 help
644 Say Y here to enable thermal driver support for the Intel Medfield
645 platform.
646
640config RAR_REGISTER 647config RAR_REGISTER
641 bool "Restricted Access Region Register Driver" 648 bool "Restricted Access Region Register Driver"
642 depends on PCI && X86_MRST 649 depends on PCI && X86_MRST
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 46f3fd2c8856..a9877711b3f1 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -30,7 +30,8 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
30obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o 30obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
31obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o 31obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
32obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o 32obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
33obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o 33obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
34obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
34obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o 35obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
35obj-$(CONFIG_INTEL_IPS) += intel_ips.o 36obj-$(CONFIG_INTEL_IPS) += intel_ips.o
36obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o 37obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
new file mode 100644
index 000000000000..1215c3096868
--- /dev/null
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -0,0 +1,575 @@
1/*
2 * intel_mid_thermal.c - Intel MID platform thermal driver
3 *
4 * Copyright (C) 2011 Intel Corporation
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20 *
21 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22 * Author: Durgadoss R <durgadoss.r@intel.com>
23 */
24
25#define pr_fmt(fmt) "intel_mid_thermal: " fmt
26
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/err.h>
30#include <linux/param.h>
31#include <linux/device.h>
32#include <linux/platform_device.h>
33#include <linux/slab.h>
34#include <linux/pm.h>
35#include <linux/thermal.h>
36
37#include <asm/intel_scu_ipc.h>
38
39/* Number of thermal sensors */
40#define MSIC_THERMAL_SENSORS 4
41
42/* ADC1 - thermal registers */
43#define MSIC_THERM_ADC1CNTL1 0x1C0
44#define MSIC_ADC_ENBL 0x10
45#define MSIC_ADC_START 0x08
46
47#define MSIC_THERM_ADC1CNTL3 0x1C2
48#define MSIC_ADCTHERM_ENBL 0x04
49#define MSIC_ADCRRDATA_ENBL 0x05
50#define MSIC_CHANL_MASK_VAL 0x0F
51
52#define MSIC_STOPBIT_MASK 16
53#define MSIC_ADCTHERM_MASK 4
54#define ADC_CHANLS_MAX 15 /* Number of ADC channels */
55#define ADC_LOOP_MAX (ADC_CHANLS_MAX - MSIC_THERMAL_SENSORS)
56
57/* ADC channel code values */
58#define SKIN_SENSOR0_CODE 0x08
59#define SKIN_SENSOR1_CODE 0x09
60#define SYS_SENSOR_CODE 0x0A
61#define MSIC_DIE_SENSOR_CODE 0x03
62
63#define SKIN_THERM_SENSOR0 0
64#define SKIN_THERM_SENSOR1 1
65#define SYS_THERM_SENSOR2 2
66#define MSIC_DIE_THERM_SENSOR3 3
67
68/* ADC code range */
69#define ADC_MAX 977
70#define ADC_MIN 162
71#define ADC_VAL0C 887
72#define ADC_VAL20C 720
73#define ADC_VAL40C 508
74#define ADC_VAL60C 315
75
76/* ADC base addresses */
77#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
78#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
79
80/* MSIC die attributes */
81#define MSIC_DIE_ADC_MIN 488
82#define MSIC_DIE_ADC_MAX 1004
83
84/* This holds the address of the first free ADC channel,
85 * among the 15 channels
86 */
87static int channel_index;
88
89struct platform_info {
90 struct platform_device *pdev;
91 struct thermal_zone_device *tzd[MSIC_THERMAL_SENSORS];
92};
93
94struct thermal_device_info {
95 unsigned int chnl_addr;
96 int direct;
97 /* This holds the current temperature in millidegree celsius */
98 long curr_temp;
99};
100
101/**
102 * to_msic_die_temp - converts adc_val to msic_die temperature
103 * @adc_val: ADC value to be converted
104 *
105 * Can sleep
106 */
107static int to_msic_die_temp(uint16_t adc_val)
108{
109 return (368 * (adc_val) / 1000) - 220;
110}
111
112/**
113 * is_valid_adc - checks whether the adc code is within the defined range
114 * @min: minimum value for the sensor
115 * @max: maximum value for the sensor
116 *
117 * Can sleep
118 */
119static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max)
120{
121 return (adc_val >= min) && (adc_val <= max);
122}
123
124/**
125 * adc_to_temp - converts the ADC code to temperature in C
126 * @direct: true if ths channel is direct index
127 * @adc_val: the adc_val that needs to be converted
128 * @tp: temperature return value
129 *
130 * Linear approximation is used to covert the skin adc value into temperature.
131 * This technique is used to avoid very long look-up table to get
132 * the appropriate temp value from ADC value.
133 * The adc code vs sensor temp curve is split into five parts
134 * to achieve very close approximate temp value with less than
135 * 0.5C error
136 */
137static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp)
138{
139 int temp;
140
141 /* Direct conversion for die temperature */
142 if (direct) {
143 if (is_valid_adc(adc_val, MSIC_DIE_ADC_MIN, MSIC_DIE_ADC_MAX)) {
144 *tp = to_msic_die_temp(adc_val) * 1000;
145 return 0;
146 }
147 return -ERANGE;
148 }
149
150 if (!is_valid_adc(adc_val, ADC_MIN, ADC_MAX))
151 return -ERANGE;
152
153 /* Linear approximation for skin temperature */
154 if (adc_val > ADC_VAL0C)
155 temp = 177 - (adc_val/5);
156 else if ((adc_val <= ADC_VAL0C) && (adc_val > ADC_VAL20C))
157 temp = 111 - (adc_val/8);
158 else if ((adc_val <= ADC_VAL20C) && (adc_val > ADC_VAL40C))
159 temp = 92 - (adc_val/10);
160 else if ((adc_val <= ADC_VAL40C) && (adc_val > ADC_VAL60C))
161 temp = 91 - (adc_val/10);
162 else
163 temp = 112 - (adc_val/6);
164
165 /* Convert temperature in celsius to milli degree celsius */
166 *tp = temp * 1000;
167 return 0;
168}
169
170/**
171 * mid_read_temp - read sensors for temperature
172 * @temp: holds the current temperature for the sensor after reading
173 *
174 * reads the adc_code from the channel and converts it to real
175 * temperature. The converted value is stored in temp.
176 *
177 * Can sleep
178 */
179static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
180{
181 struct thermal_device_info *td_info = tzd->devdata;
182 uint16_t adc_val, addr;
183 uint8_t data = 0;
184 int ret;
185 unsigned long curr_temp;
186
187
188 addr = td_info->chnl_addr;
189
190 /* Enable the msic for conversion before reading */
191 ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL);
192 if (ret)
193 return ret;
194
195 /* Re-toggle the RRDATARD bit (temporary workaround) */
196 ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL);
197 if (ret)
198 return ret;
199
200 /* Read the higher bits of data */
201 ret = intel_scu_ipc_ioread8(addr, &data);
202 if (ret)
203 return ret;
204
205 /* Shift bits to accomodate the lower two data bits */
206 adc_val = (data << 2);
207 addr++;
208
209 ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */
210 if (ret)
211 return ret;
212
213 /* Adding lower two bits to the higher bits */
214 data &= 03;
215 adc_val += data;
216
217 /* Convert ADC value to temperature */
218 ret = adc_to_temp(td_info->direct, adc_val, &curr_temp);
219 if (ret == 0)
220 *temp = td_info->curr_temp = curr_temp;
221 return ret;
222}
223
224/**
225 * configure_adc - enables/disables the ADC for conversion
226 * @val: zero: disables the ADC non-zero:enables the ADC
227 *
228 * Enable/Disable the ADC depending on the argument
229 *
230 * Can sleep
231 */
232static int configure_adc(int val)
233{
234 int ret;
235 uint8_t data;
236
237 ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
238 if (ret)
239 return ret;
240
241 if (val) {
242 /* Enable and start the ADC */
243 data |= (MSIC_ADC_ENBL | MSIC_ADC_START);
244 } else {
245 /* Just stop the ADC */
246 data &= (~MSIC_ADC_START);
247 }
248
249 return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data);
250}
251
252/**
253 * set_up_therm_channel - enable thermal channel for conversion
254 * @base_addr: index of free msic ADC channel
255 *
256 * Enable all the three channels for conversion
257 *
258 * Can sleep
259 */
260static int set_up_therm_channel(u16 base_addr)
261{
262 int ret;
263
264 /* Enable all the sensor channels */
265 ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE);
266 if (ret)
267 return ret;
268
269 ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE);
270 if (ret)
271 return ret;
272
273 ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE);
274 if (ret)
275 return ret;
276
277 /* Since this is the last channel, set the stop bit
278 to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
279 ret = intel_scu_ipc_iowrite8(base_addr + 3,
280 (MSIC_DIE_SENSOR_CODE | 0x10));
281 if (ret)
282 return ret;
283
284 /* Enable ADC and start it */
285 return configure_adc(1);
286}
287
288/**
289 * reset_stopbit - sets the stop bit to 0 on the given channel
290 * @addr: address of the channel
291 *
292 * Can sleep
293 */
294static int reset_stopbit(uint16_t addr)
295{
296 int ret;
297 uint8_t data;
298 ret = intel_scu_ipc_ioread8(addr, &data);
299 if (ret)
300 return ret;
301 /* Set the stop bit to zero */
302 return intel_scu_ipc_iowrite8(addr, (data & 0xEF));
303}
304
305/**
306 * find_free_channel - finds an empty channel for conversion
307 *
308 * If the ADC is not enabled then start using 0th channel
309 * itself. Otherwise find an empty channel by looking for a
310 * channel in which the stopbit is set to 1. returns the index
311 * of the first free channel if succeeds or an error code.
312 *
313 * Context: can sleep
314 *
315 * FIXME: Ultimately the channel allocator will move into the intel_scu_ipc
316 * code.
317 */
318static int find_free_channel(void)
319{
320 int ret;
321 int i;
322 uint8_t data;
323
324 /* check whether ADC is enabled */
325 ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
326 if (ret)
327 return ret;
328
329 if ((data & MSIC_ADC_ENBL) == 0)
330 return 0;
331
332 /* ADC is already enabled; Looking for an empty channel */
333 for (i = 0; i < ADC_CHANLS_MAX; i++) {
334 ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data);
335 if (ret)
336 return ret;
337
338 if (data & MSIC_STOPBIT_MASK) {
339 ret = i;
340 break;
341 }
342 }
343 return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret;
344}
345
346/**
347 * mid_initialize_adc - initializing the ADC
348 * @dev: our device structure
349 *
350 * Initialize the ADC for reading thermistor values. Can sleep.
351 */
352static int mid_initialize_adc(struct device *dev)
353{
354 u8 data;
355 u16 base_addr;
356 int ret;
357
358 /*
359 * Ensure that adctherm is disabled before we
360 * initialize the ADC
361 */
362 ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data);
363 if (ret)
364 return ret;
365
366 if (data & MSIC_ADCTHERM_MASK)
367 dev_warn(dev, "ADCTHERM already set");
368
369 /* Index of the first channel in which the stop bit is set */
370 channel_index = find_free_channel();
371 if (channel_index < 0) {
372 dev_err(dev, "No free ADC channels");
373 return channel_index;
374 }
375
376 base_addr = ADC_CHNL_START_ADDR + channel_index;
377
378 if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) {
379 /* Reset stop bit for channels other than 0 and 12 */
380 ret = reset_stopbit(base_addr);
381 if (ret)
382 return ret;
383
384 /* Index of the first free channel */
385 base_addr++;
386 channel_index++;
387 }
388
389 ret = set_up_therm_channel(base_addr);
390 if (ret) {
391 dev_err(dev, "unable to enable ADC");
392 return ret;
393 }
394 dev_dbg(dev, "ADC initialization successful");
395 return ret;
396}
397
398/**
399 * initialize_sensor - sets default temp and timer ranges
400 * @index: index of the sensor
401 *
402 * Context: can sleep
403 */
404static struct thermal_device_info *initialize_sensor(int index)
405{
406 struct thermal_device_info *td_info =
407 kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL);
408
409 if (!td_info)
410 return NULL;
411
412 /* Set the base addr of the channel for this sensor */
413 td_info->chnl_addr = ADC_DATA_START_ADDR + 2 * (channel_index + index);
414 /* Sensor 3 is direct conversion */
415 if (index == 3)
416 td_info->direct = 1;
417 return td_info;
418}
419
420/**
421 * mid_thermal_resume - resume routine
422 * @pdev: platform device structure
423 *
424 * mid thermal resume: re-initializes the adc. Can sleep.
425 */
426static int mid_thermal_resume(struct platform_device *pdev)
427{
428 return mid_initialize_adc(&pdev->dev);
429}
430
431/**
432 * mid_thermal_suspend - suspend routine
433 * @pdev: platform device structure
434 *
435 * mid thermal suspend implements the suspend functionality
436 * by stopping the ADC. Can sleep.
437 */
438static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg)
439{
440 /*
441 * This just stops the ADC and does not disable it.
442 * temporary workaround until we have a generic ADC driver.
443 * If 0 is passed, it disables the ADC.
444 */
445 return configure_adc(0);
446}
447
448/**
449 * read_curr_temp - reads the current temperature and stores in temp
450 * @temp: holds the current temperature value after reading
451 *
452 * Can sleep
453 */
454static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp)
455{
456 WARN_ON(tzd == NULL);
457 return mid_read_temp(tzd, temp);
458}
459
460/* Can't be const */
461static struct thermal_zone_device_ops tzd_ops = {
462 .get_temp = read_curr_temp,
463};
464
465
466/**
467 * mid_thermal_probe - mfld thermal initialize
468 * @pdev: platform device structure
469 *
470 * mid thermal probe initializes the hardware and registers
471 * all the sensors with the generic thermal framework. Can sleep.
472 */
473static int mid_thermal_probe(struct platform_device *pdev)
474{
475 static char *name[MSIC_THERMAL_SENSORS] = {
476 "skin0", "skin1", "sys", "msicdie"
477 };
478
479 int ret;
480 int i;
481 struct platform_info *pinfo;
482
483 pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL);
484 if (!pinfo)
485 return -ENOMEM;
486
487 /* Initializing the hardware */
488 ret = mid_initialize_adc(&pdev->dev);
489 if (ret) {
490 dev_err(&pdev->dev, "ADC init failed");
491 kfree(pinfo);
492 return ret;
493 }
494
495 /* Register each sensor with the generic thermal framework*/
496 for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
497 pinfo->tzd[i] = thermal_zone_device_register(name[i],
498 0, initialize_sensor(i),
499 &tzd_ops, 0, 0, 0, 0);
500 if (IS_ERR(pinfo->tzd[i]))
501 goto reg_fail;
502 }
503
504 pinfo->pdev = pdev;
505 platform_set_drvdata(pdev, pinfo);
506 return 0;
507
508reg_fail:
509 ret = PTR_ERR(pinfo->tzd[i]);
510 while (--i >= 0)
511 thermal_zone_device_unregister(pinfo->tzd[i]);
512 configure_adc(0);
513 kfree(pinfo);
514 return ret;
515}
516
517/**
518 * mid_thermal_remove - mfld thermal finalize
519 * @dev: platform device structure
520 *
521 * MLFD thermal remove unregisters all the sensors from the generic
522 * thermal framework. Can sleep.
523 */
524static int mid_thermal_remove(struct platform_device *pdev)
525{
526 int i;
527 struct platform_info *pinfo = platform_get_drvdata(pdev);
528
529 for (i = 0; i < MSIC_THERMAL_SENSORS; i++)
530 thermal_zone_device_unregister(pinfo->tzd[i]);
531
532 platform_set_drvdata(pdev, NULL);
533
534 /* Stop the ADC */
535 return configure_adc(0);
536}
537
538/*********************************************************************
539 * Driver initialisation and finalization
540 *********************************************************************/
541
542#define DRIVER_NAME "msic_sensor"
543
544static const struct platform_device_id therm_id_table[] = {
545 { DRIVER_NAME, 1 },
546};
547
548static struct platform_driver mid_thermal_driver = {
549 .driver = {
550 .name = DRIVER_NAME,
551 .owner = THIS_MODULE,
552 },
553 .probe = mid_thermal_probe,
554 .suspend = mid_thermal_suspend,
555 .resume = mid_thermal_resume,
556 .remove = __devexit_p(mid_thermal_remove),
557 .id_table = therm_id_table,
558};
559
560static int __init mid_thermal_module_init(void)
561{
562 return platform_driver_register(&mid_thermal_driver);
563}
564
565static void __exit mid_thermal_module_exit(void)
566{
567 platform_driver_unregister(&mid_thermal_driver);
568}
569
570module_init(mid_thermal_module_init);
571module_exit(mid_thermal_module_exit);
572
573MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
574MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver");
575MODULE_LICENSE("GPL");