linux/drivers/macintosh/windfarm_lm87_sensor.c
<<
>>
Prefs
   1/*
   2 * Windfarm PowerMac thermal control. LM87 sensor
   3 *
   4 * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
   5 *
   6 * Released under the term of the GNU GPL v2.
   7 *
   8 */
   9
  10#include <linux/types.h>
  11#include <linux/errno.h>
  12#include <linux/kernel.h>
  13#include <linux/delay.h>
  14#include <linux/slab.h>
  15#include <linux/init.h>
  16#include <linux/wait.h>
  17#include <linux/i2c.h>
  18#include <asm/prom.h>
  19#include <asm/machdep.h>
  20#include <asm/io.h>
  21#include <asm/sections.h>
  22#include <asm/pmac_low_i2c.h>
  23
  24#include "windfarm.h"
  25
  26#define VERSION "1.0"
  27
  28#undef DEBUG
  29
  30#ifdef DEBUG
  31#define DBG(args...)    printk(args)
  32#else
  33#define DBG(args...)    do { } while(0)
  34#endif
  35
  36struct wf_lm87_sensor {
  37        struct i2c_client       *i2c;
  38        struct wf_sensor        sens;
  39};
  40#define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens)
  41
  42
  43static int wf_lm87_read_reg(struct i2c_client *chip, int reg)
  44{
  45        int rc, tries = 0;
  46        u8 buf;
  47
  48        for (;;) {
  49                /* Set address */
  50                buf = (u8)reg;
  51                rc = i2c_master_send(chip, &buf, 1);
  52                if (rc <= 0)
  53                        goto error;
  54                rc = i2c_master_recv(chip, &buf, 1);
  55                if (rc <= 0)
  56                        goto error;
  57                return (int)buf;
  58        error:
  59                DBG("wf_lm87: Error reading LM87, retrying...\n");
  60                if (++tries > 10) {
  61                        printk(KERN_ERR "wf_lm87: Error reading LM87 !\n");
  62                        return -EIO;
  63                }
  64                msleep(10);
  65        }
  66}
  67
  68static int wf_lm87_get(struct wf_sensor *sr, s32 *value)
  69{
  70        struct wf_lm87_sensor *lm = sr->priv;
  71        s32 temp;
  72
  73        if (lm->i2c == NULL)
  74                return -ENODEV;
  75
  76#define LM87_INT_TEMP           0x27
  77
  78        /* Read temperature register */
  79        temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP);
  80        if (temp < 0)
  81                return temp;
  82        *value = temp << 16;
  83
  84        return 0;
  85}
  86
  87static void wf_lm87_release(struct wf_sensor *sr)
  88{
  89        struct wf_lm87_sensor *lm = wf_to_lm87(sr);
  90
  91        kfree(lm);
  92}
  93
  94static struct wf_sensor_ops wf_lm87_ops = {
  95        .get_value      = wf_lm87_get,
  96        .release        = wf_lm87_release,
  97        .owner          = THIS_MODULE,
  98};
  99
 100static int wf_lm87_probe(struct i2c_client *client,
 101                         const struct i2c_device_id *id)
 102{       
 103        struct wf_lm87_sensor *lm;
 104        const char *name = NULL, *loc;
 105        struct device_node *np = NULL;
 106        int rc;
 107
 108        /*
 109         * The lm87 contains a whole pile of sensors, additionally,
 110         * the Xserve G5 has several lm87's. However, for now we only
 111         * care about the internal temperature sensor
 112         */
 113        while ((np = of_get_next_child(client->dev.of_node, np)) != NULL) {
 114                if (strcmp(np->name, "int-temp"))
 115                        continue;
 116                loc = of_get_property(np, "location", NULL);
 117                if (!loc)
 118                        continue;
 119                if (strstr(loc, "DIMM"))
 120                        name = "dimms-temp";
 121                else if (strstr(loc, "Processors"))
 122                        name = "between-cpus-temp";
 123                if (name) {
 124                        of_node_put(np);
 125                        break;
 126                }
 127        }
 128        if (!name) {
 129                pr_warning("wf_lm87: Unsupported sensor %s\n",
 130                           client->dev.of_node->full_name);
 131                return -ENODEV;
 132        }
 133
 134        lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL);
 135        if (lm == NULL)
 136                return -ENODEV;
 137
 138        lm->i2c = client;
 139        lm->sens.name = name;
 140        lm->sens.ops = &wf_lm87_ops;
 141        lm->sens.priv = lm;
 142        i2c_set_clientdata(client, lm);
 143
 144        rc = wf_register_sensor(&lm->sens);
 145        if (rc)
 146                kfree(lm);
 147        return rc;
 148}
 149
 150static int wf_lm87_remove(struct i2c_client *client)
 151{
 152        struct wf_lm87_sensor *lm = i2c_get_clientdata(client);
 153
 154        DBG("wf_lm87: i2c detatch called for %s\n", lm->sens.name);
 155
 156        /* Mark client detached */
 157        lm->i2c = NULL;
 158
 159        /* release sensor */
 160        wf_unregister_sensor(&lm->sens);
 161
 162        return 0;
 163}
 164
 165static const struct i2c_device_id wf_lm87_id[] = {
 166        { "MAC,lm87cimt", 0 },
 167        { }
 168};
 169MODULE_DEVICE_TABLE(i2c, wf_lm87_id);
 170
 171static struct i2c_driver wf_lm87_driver = {
 172        .driver = {
 173                .name   = "wf_lm87",
 174        },
 175        .probe          = wf_lm87_probe,
 176        .remove         = wf_lm87_remove,
 177        .id_table       = wf_lm87_id,
 178};
 179
 180static int __init wf_lm87_sensor_init(void)
 181{
 182        /* We only support this on the Xserve */
 183        if (!of_machine_is_compatible("RackMac3,1"))
 184                return -ENODEV;
 185
 186        return i2c_add_driver(&wf_lm87_driver);
 187}
 188
 189static void __exit wf_lm87_sensor_exit(void)
 190{
 191        i2c_del_driver(&wf_lm87_driver);
 192}
 193
 194
 195module_init(wf_lm87_sensor_init);
 196module_exit(wf_lm87_sensor_exit);
 197
 198MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 199MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control");
 200MODULE_LICENSE("GPL");
 201
 202