linux/drivers/hwmon/exynos4_tmu.c
<<
>>
Prefs
   1/*
   2 * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit)
   3 *
   4 *  Copyright (C) 2011 Samsung Electronics
   5 *  Donggeun Kim <dg77.kim@samsung.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 *
  21 */
  22
  23#include <linux/module.h>
  24#include <linux/err.h>
  25#include <linux/kernel.h>
  26#include <linux/slab.h>
  27#include <linux/platform_device.h>
  28#include <linux/interrupt.h>
  29#include <linux/clk.h>
  30#include <linux/workqueue.h>
  31#include <linux/sysfs.h>
  32#include <linux/kobject.h>
  33#include <linux/io.h>
  34#include <linux/mutex.h>
  35
  36#include <linux/hwmon.h>
  37#include <linux/hwmon-sysfs.h>
  38
  39#include <linux/platform_data/exynos4_tmu.h>
  40
  41#define EXYNOS4_TMU_REG_TRIMINFO        0x0
  42#define EXYNOS4_TMU_REG_CONTROL         0x20
  43#define EXYNOS4_TMU_REG_STATUS          0x28
  44#define EXYNOS4_TMU_REG_CURRENT_TEMP    0x40
  45#define EXYNOS4_TMU_REG_THRESHOLD_TEMP  0x44
  46#define EXYNOS4_TMU_REG_TRIG_LEVEL0     0x50
  47#define EXYNOS4_TMU_REG_TRIG_LEVEL1     0x54
  48#define EXYNOS4_TMU_REG_TRIG_LEVEL2     0x58
  49#define EXYNOS4_TMU_REG_TRIG_LEVEL3     0x5C
  50#define EXYNOS4_TMU_REG_PAST_TEMP0      0x60
  51#define EXYNOS4_TMU_REG_PAST_TEMP1      0x64
  52#define EXYNOS4_TMU_REG_PAST_TEMP2      0x68
  53#define EXYNOS4_TMU_REG_PAST_TEMP3      0x6C
  54#define EXYNOS4_TMU_REG_INTEN           0x70
  55#define EXYNOS4_TMU_REG_INTSTAT         0x74
  56#define EXYNOS4_TMU_REG_INTCLEAR        0x78
  57
  58#define EXYNOS4_TMU_GAIN_SHIFT          8
  59#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT   24
  60
  61#define EXYNOS4_TMU_TRIM_TEMP_MASK      0xff
  62#define EXYNOS4_TMU_CORE_ON     3
  63#define EXYNOS4_TMU_CORE_OFF    2
  64#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET     50
  65#define EXYNOS4_TMU_TRIG_LEVEL0_MASK    0x1
  66#define EXYNOS4_TMU_TRIG_LEVEL1_MASK    0x10
  67#define EXYNOS4_TMU_TRIG_LEVEL2_MASK    0x100
  68#define EXYNOS4_TMU_TRIG_LEVEL3_MASK    0x1000
  69#define EXYNOS4_TMU_INTCLEAR_VAL        0x1111
  70
  71struct exynos4_tmu_data {
  72        struct exynos4_tmu_platform_data *pdata;
  73        struct device *hwmon_dev;
  74        struct resource *mem;
  75        void __iomem *base;
  76        int irq;
  77        struct work_struct irq_work;
  78        struct mutex lock;
  79        struct clk *clk;
  80        u8 temp_error1, temp_error2;
  81};
  82
  83/*
  84 * TMU treats temperature as a mapped temperature code.
  85 * The temperature is converted differently depending on the calibration type.
  86 */
  87static int temp_to_code(struct exynos4_tmu_data *data, u8 temp)
  88{
  89        struct exynos4_tmu_platform_data *pdata = data->pdata;
  90        int temp_code;
  91
  92        /* temp should range between 25 and 125 */
  93        if (temp < 25 || temp > 125) {
  94                temp_code = -EINVAL;
  95                goto out;
  96        }
  97
  98        switch (pdata->cal_type) {
  99        case TYPE_TWO_POINT_TRIMMING:
 100                temp_code = (temp - 25) *
 101                    (data->temp_error2 - data->temp_error1) /
 102                    (85 - 25) + data->temp_error1;
 103                break;
 104        case TYPE_ONE_POINT_TRIMMING:
 105                temp_code = temp + data->temp_error1 - 25;
 106                break;
 107        default:
 108                temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
 109                break;
 110        }
 111out:
 112        return temp_code;
 113}
 114
 115/*
 116 * Calculate a temperature value from a temperature code.
 117 * The unit of the temperature is degree Celsius.
 118 */
 119static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code)
 120{
 121        struct exynos4_tmu_platform_data *pdata = data->pdata;
 122        int temp;
 123
 124        /* temp_code should range between 75 and 175 */
 125        if (temp_code < 75 || temp_code > 175) {
 126                temp = -ENODATA;
 127                goto out;
 128        }
 129
 130        switch (pdata->cal_type) {
 131        case TYPE_TWO_POINT_TRIMMING:
 132                temp = (temp_code - data->temp_error1) * (85 - 25) /
 133                    (data->temp_error2 - data->temp_error1) + 25;
 134                break;
 135        case TYPE_ONE_POINT_TRIMMING:
 136                temp = temp_code - data->temp_error1 + 25;
 137                break;
 138        default:
 139                temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET;
 140                break;
 141        }
 142out:
 143        return temp;
 144}
 145
 146static int exynos4_tmu_initialize(struct platform_device *pdev)
 147{
 148        struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
 149        struct exynos4_tmu_platform_data *pdata = data->pdata;
 150        unsigned int status, trim_info;
 151        int ret = 0, threshold_code;
 152
 153        mutex_lock(&data->lock);
 154        clk_enable(data->clk);
 155
 156        status = readb(data->base + EXYNOS4_TMU_REG_STATUS);
 157        if (!status) {
 158                ret = -EBUSY;
 159                goto out;
 160        }
 161
 162        /* Save trimming info in order to perform calibration */
 163        trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO);
 164        data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK;
 165        data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK);
 166
 167        /* Write temperature code for threshold */
 168        threshold_code = temp_to_code(data, pdata->threshold);
 169        if (threshold_code < 0) {
 170                ret = threshold_code;
 171                goto out;
 172        }
 173        writeb(threshold_code,
 174                data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP);
 175
 176        writeb(pdata->trigger_levels[0],
 177                data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0);
 178        writeb(pdata->trigger_levels[1],
 179                data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1);
 180        writeb(pdata->trigger_levels[2],
 181                data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2);
 182        writeb(pdata->trigger_levels[3],
 183                data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3);
 184
 185        writel(EXYNOS4_TMU_INTCLEAR_VAL,
 186                data->base + EXYNOS4_TMU_REG_INTCLEAR);
 187out:
 188        clk_disable(data->clk);
 189        mutex_unlock(&data->lock);
 190
 191        return ret;
 192}
 193
 194static void exynos4_tmu_control(struct platform_device *pdev, bool on)
 195{
 196        struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
 197        struct exynos4_tmu_platform_data *pdata = data->pdata;
 198        unsigned int con, interrupt_en;
 199
 200        mutex_lock(&data->lock);
 201        clk_enable(data->clk);
 202
 203        con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT |
 204                pdata->gain << EXYNOS4_TMU_GAIN_SHIFT;
 205        if (on) {
 206                con |= EXYNOS4_TMU_CORE_ON;
 207                interrupt_en = pdata->trigger_level3_en << 12 |
 208                        pdata->trigger_level2_en << 8 |
 209                        pdata->trigger_level1_en << 4 |
 210                        pdata->trigger_level0_en;
 211        } else {
 212                con |= EXYNOS4_TMU_CORE_OFF;
 213                interrupt_en = 0; /* Disable all interrupts */
 214        }
 215        writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN);
 216        writel(con, data->base + EXYNOS4_TMU_REG_CONTROL);
 217
 218        clk_disable(data->clk);
 219        mutex_unlock(&data->lock);
 220}
 221
 222static int exynos4_tmu_read(struct exynos4_tmu_data *data)
 223{
 224        u8 temp_code;
 225        int temp;
 226
 227        mutex_lock(&data->lock);
 228        clk_enable(data->clk);
 229
 230        temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP);
 231        temp = code_to_temp(data, temp_code);
 232
 233        clk_disable(data->clk);
 234        mutex_unlock(&data->lock);
 235
 236        return temp;
 237}
 238
 239static void exynos4_tmu_work(struct work_struct *work)
 240{
 241        struct exynos4_tmu_data *data = container_of(work,
 242                        struct exynos4_tmu_data, irq_work);
 243
 244        mutex_lock(&data->lock);
 245        clk_enable(data->clk);
 246
 247        writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR);
 248
 249        kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE);
 250
 251        enable_irq(data->irq);
 252
 253        clk_disable(data->clk);
 254        mutex_unlock(&data->lock);
 255}
 256
 257static irqreturn_t exynos4_tmu_irq(int irq, void *id)
 258{
 259        struct exynos4_tmu_data *data = id;
 260
 261        disable_irq_nosync(irq);
 262        schedule_work(&data->irq_work);
 263
 264        return IRQ_HANDLED;
 265}
 266
 267static ssize_t exynos4_tmu_show_name(struct device *dev,
 268                struct device_attribute *attr, char *buf)
 269{
 270        return sprintf(buf, "exynos4-tmu\n");
 271}
 272
 273static ssize_t exynos4_tmu_show_temp(struct device *dev,
 274                struct device_attribute *attr, char *buf)
 275{
 276        struct exynos4_tmu_data *data = dev_get_drvdata(dev);
 277        int ret;
 278
 279        ret = exynos4_tmu_read(data);
 280        if (ret < 0)
 281                return ret;
 282
 283        /* convert from degree Celsius to millidegree Celsius */
 284        return sprintf(buf, "%d\n", ret * 1000);
 285}
 286
 287static ssize_t exynos4_tmu_show_alarm(struct device *dev,
 288                struct device_attribute *devattr, char *buf)
 289{
 290        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 291        struct exynos4_tmu_data *data = dev_get_drvdata(dev);
 292        struct exynos4_tmu_platform_data *pdata = data->pdata;
 293        int temp;
 294        unsigned int trigger_level;
 295
 296        temp = exynos4_tmu_read(data);
 297        if (temp < 0)
 298                return temp;
 299
 300        trigger_level = pdata->threshold + pdata->trigger_levels[attr->index];
 301
 302        return sprintf(buf, "%d\n", !!(temp > trigger_level));
 303}
 304
 305static ssize_t exynos4_tmu_show_level(struct device *dev,
 306                struct device_attribute *devattr, char *buf)
 307{
 308        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 309        struct exynos4_tmu_data *data = dev_get_drvdata(dev);
 310        struct exynos4_tmu_platform_data *pdata = data->pdata;
 311        unsigned int temp = pdata->threshold +
 312                        pdata->trigger_levels[attr->index];
 313
 314        return sprintf(buf, "%u\n", temp * 1000);
 315}
 316
 317static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL);
 318static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0);
 319
 320static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
 321                exynos4_tmu_show_alarm, NULL, 1);
 322static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
 323                exynos4_tmu_show_alarm, NULL, 2);
 324static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO,
 325                exynos4_tmu_show_alarm, NULL, 3);
 326
 327static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1);
 328static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2);
 329static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO,
 330                exynos4_tmu_show_level, NULL, 3);
 331
 332static struct attribute *exynos4_tmu_attributes[] = {
 333        &dev_attr_name.attr,
 334        &sensor_dev_attr_temp1_input.dev_attr.attr,
 335        &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
 336        &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
 337        &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
 338        &sensor_dev_attr_temp1_max.dev_attr.attr,
 339        &sensor_dev_attr_temp1_crit.dev_attr.attr,
 340        &sensor_dev_attr_temp1_emergency.dev_attr.attr,
 341        NULL,
 342};
 343
 344static const struct attribute_group exynos4_tmu_attr_group = {
 345        .attrs = exynos4_tmu_attributes,
 346};
 347
 348static int __devinit exynos4_tmu_probe(struct platform_device *pdev)
 349{
 350        struct exynos4_tmu_data *data;
 351        struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data;
 352        int ret;
 353
 354        if (!pdata) {
 355                dev_err(&pdev->dev, "No platform init data supplied.\n");
 356                return -ENODEV;
 357        }
 358
 359        data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL);
 360        if (!data) {
 361                dev_err(&pdev->dev, "Failed to allocate driver structure\n");
 362                return -ENOMEM;
 363        }
 364
 365        data->irq = platform_get_irq(pdev, 0);
 366        if (data->irq < 0) {
 367                ret = data->irq;
 368                dev_err(&pdev->dev, "Failed to get platform irq\n");
 369                goto err_free;
 370        }
 371
 372        INIT_WORK(&data->irq_work, exynos4_tmu_work);
 373
 374        data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 375        if (!data->mem) {
 376                ret = -ENOENT;
 377                dev_err(&pdev->dev, "Failed to get platform resource\n");
 378                goto err_free;
 379        }
 380
 381        data->mem = request_mem_region(data->mem->start,
 382                        resource_size(data->mem), pdev->name);
 383        if (!data->mem) {
 384                ret = -ENODEV;
 385                dev_err(&pdev->dev, "Failed to request memory region\n");
 386                goto err_free;
 387        }
 388
 389        data->base = ioremap(data->mem->start, resource_size(data->mem));
 390        if (!data->base) {
 391                ret = -ENODEV;
 392                dev_err(&pdev->dev, "Failed to ioremap memory\n");
 393                goto err_mem_region;
 394        }
 395
 396        ret = request_irq(data->irq, exynos4_tmu_irq,
 397                IRQF_TRIGGER_RISING,
 398                "exynos4-tmu", data);
 399        if (ret) {
 400                dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
 401                goto err_io_remap;
 402        }
 403
 404        data->clk = clk_get(NULL, "tmu_apbif");
 405        if (IS_ERR(data->clk)) {
 406                ret = PTR_ERR(data->clk);
 407                dev_err(&pdev->dev, "Failed to get clock\n");
 408                goto err_irq;
 409        }
 410
 411        data->pdata = pdata;
 412        platform_set_drvdata(pdev, data);
 413        mutex_init(&data->lock);
 414
 415        ret = exynos4_tmu_initialize(pdev);
 416        if (ret) {
 417                dev_err(&pdev->dev, "Failed to initialize TMU\n");
 418                goto err_clk;
 419        }
 420
 421        ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
 422        if (ret) {
 423                dev_err(&pdev->dev, "Failed to create sysfs group\n");
 424                goto err_clk;
 425        }
 426
 427        data->hwmon_dev = hwmon_device_register(&pdev->dev);
 428        if (IS_ERR(data->hwmon_dev)) {
 429                ret = PTR_ERR(data->hwmon_dev);
 430                dev_err(&pdev->dev, "Failed to register hwmon device\n");
 431                goto err_create_group;
 432        }
 433
 434        exynos4_tmu_control(pdev, true);
 435
 436        return 0;
 437
 438err_create_group:
 439        sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
 440err_clk:
 441        platform_set_drvdata(pdev, NULL);
 442        clk_put(data->clk);
 443err_irq:
 444        free_irq(data->irq, data);
 445err_io_remap:
 446        iounmap(data->base);
 447err_mem_region:
 448        release_mem_region(data->mem->start, resource_size(data->mem));
 449err_free:
 450        kfree(data);
 451
 452        return ret;
 453}
 454
 455static int __devexit exynos4_tmu_remove(struct platform_device *pdev)
 456{
 457        struct exynos4_tmu_data *data = platform_get_drvdata(pdev);
 458
 459        exynos4_tmu_control(pdev, false);
 460
 461        hwmon_device_unregister(data->hwmon_dev);
 462        sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group);
 463
 464        clk_put(data->clk);
 465
 466        free_irq(data->irq, data);
 467
 468        iounmap(data->base);
 469        release_mem_region(data->mem->start, resource_size(data->mem));
 470
 471        platform_set_drvdata(pdev, NULL);
 472
 473        kfree(data);
 474
 475        return 0;
 476}
 477
 478#ifdef CONFIG_PM_SLEEP
 479static int exynos4_tmu_suspend(struct device *dev)
 480{
 481        exynos4_tmu_control(to_platform_device(dev), false);
 482
 483        return 0;
 484}
 485
 486static int exynos4_tmu_resume(struct device *dev)
 487{
 488        struct platform_device *pdev = to_platform_device(dev);
 489
 490        exynos4_tmu_initialize(pdev);
 491        exynos4_tmu_control(pdev, true);
 492
 493        return 0;
 494}
 495
 496static SIMPLE_DEV_PM_OPS(exynos4_tmu_pm,
 497                         exynos4_tmu_suspend, exynos4_tmu_resume);
 498#define EXYNOS4_TMU_PM  &exynos4_tmu_pm
 499#else
 500#define EXYNOS4_TMU_PM  NULL
 501#endif
 502
 503static struct platform_driver exynos4_tmu_driver = {
 504        .driver = {
 505                .name   = "exynos4-tmu",
 506                .owner  = THIS_MODULE,
 507                .pm     = EXYNOS4_TMU_PM,
 508        },
 509        .probe = exynos4_tmu_probe,
 510        .remove = __devexit_p(exynos4_tmu_remove),
 511};
 512
 513module_platform_driver(exynos4_tmu_driver);
 514
 515MODULE_DESCRIPTION("EXYNOS4 TMU Driver");
 516MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
 517MODULE_LICENSE("GPL");
 518MODULE_ALIAS("platform:exynos4-tmu");
 519
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.