linux/drivers/macintosh/windfarm_ad7417_sensor.c
<<
>>
Prefs
   1/*
   2 * Windfarm PowerMac thermal control. AD7417 sensors
   3 *
   4 * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
   5 *
   6 * Released under the term of the GNU GPL v2.
   7 */
   8
   9#include <linux/types.h>
  10#include <linux/errno.h>
  11#include <linux/kernel.h>
  12#include <linux/delay.h>
  13#include <linux/slab.h>
  14#include <linux/init.h>
  15#include <linux/wait.h>
  16#include <linux/i2c.h>
  17#include <asm/prom.h>
  18#include <asm/machdep.h>
  19#include <asm/io.h>
  20#include <asm/sections.h>
  21
  22#include "windfarm.h"
  23#include "windfarm_mpu.h"
  24
  25#define VERSION "1.0"
  26
  27struct wf_ad7417_priv {
  28        struct kref             ref;
  29        struct i2c_client       *i2c;
  30        u8                      config;
  31        u8                      cpu;
  32        const struct mpu_data   *mpu;
  33        struct wf_sensor        sensors[5];
  34        struct mutex            lock;
  35};
  36
  37static int wf_ad7417_temp_get(struct wf_sensor *sr, s32 *value)
  38{
  39        struct wf_ad7417_priv *pv = sr->priv;
  40        u8 buf[2];
  41        s16 raw;
  42        int rc;
  43
  44        *value = 0;
  45        mutex_lock(&pv->lock);
  46
  47        /* Read temp register */
  48        buf[0] = 0;
  49        rc = i2c_master_send(pv->i2c, buf, 1);
  50        if (rc < 0)
  51                goto error;
  52        rc = i2c_master_recv(pv->i2c, buf, 2);
  53        if (rc < 0)
  54                goto error;
  55
  56        /* Read a a 16-bit signed value */
  57        raw = be16_to_cpup((__le16 *)buf);
  58
  59        /* Convert 8.8-bit to 16.16 fixed point */
  60        *value = ((s32)raw) << 8;
  61
  62        mutex_unlock(&pv->lock);
  63        return 0;
  64
  65error:
  66        mutex_unlock(&pv->lock);
  67        return -1;
  68}
  69
  70/*
  71 * Scaling factors for the AD7417 ADC converters (except
  72 * for the CPU diode which is obtained from the EEPROM).
  73 * Those values are obtained from the property list of
  74 * the darwin driver
  75 */
  76#define ADC_12V_CURRENT_SCALE   0x0320  /* _AD2 */
  77#define ADC_CPU_VOLTAGE_SCALE   0x00a0  /* _AD3 */
  78#define ADC_CPU_CURRENT_SCALE   0x1f40  /* _AD4 */
  79
  80static void wf_ad7417_adc_convert(struct wf_ad7417_priv *pv,
  81                                  int chan, s32 raw, s32 *value)
  82{
  83        switch(chan) {
  84        case 1: /* Diode */
  85                *value = (raw * (s32)pv->mpu->mdiode +
  86                        ((s32)pv->mpu->bdiode << 12)) >> 2;
  87                break;
  88        case 2: /* 12v current */
  89                *value = raw * ADC_12V_CURRENT_SCALE;
  90                break;
  91        case 3: /* core voltage */
  92                *value = raw * ADC_CPU_VOLTAGE_SCALE;
  93                break;
  94        case 4: /* core current */
  95                *value = raw * ADC_CPU_CURRENT_SCALE;
  96                break;
  97        }
  98}
  99
 100static int wf_ad7417_adc_get(struct wf_sensor *sr, s32 *value)
 101{
 102        struct wf_ad7417_priv *pv = sr->priv;
 103        int chan = sr - pv->sensors;
 104        int i, rc;
 105        u8 buf[2];
 106        u16 raw;
 107
 108        *value = 0;
 109        mutex_lock(&pv->lock);
 110        for (i = 0; i < 10; i++) {
 111                /* Set channel */
 112                buf[0] = 1;
 113                buf[1] = (pv->config & 0x1f) | (chan << 5);
 114                rc = i2c_master_send(pv->i2c, buf, 2);
 115                if (rc < 0)
 116                        goto error;
 117
 118                /* Wait for conversion */
 119                msleep(1);
 120
 121                /* Switch to data register */
 122                buf[0] = 4;
 123                rc = i2c_master_send(pv->i2c, buf, 1);
 124                if (rc < 0)
 125                        goto error;
 126
 127                /* Read result */
 128                rc = i2c_master_recv(pv->i2c, buf, 2);
 129                if (rc < 0)
 130                        goto error;
 131
 132                /* Read a a 16-bit signed value */
 133                raw = be16_to_cpup((__le16 *)buf) >> 6;
 134                wf_ad7417_adc_convert(pv, chan, raw, value);
 135
 136                dev_vdbg(&pv->i2c->dev, "ADC chan %d [%s]"
 137                         " raw value: 0x%x, conv to: 0x%08x\n",
 138                         chan, sr->name, raw, *value);
 139
 140                mutex_unlock(&pv->lock);
 141                return 0;
 142
 143        error:
 144                dev_dbg(&pv->i2c->dev,
 145                          "Error reading ADC, try %d...\n", i);
 146                if (i < 9)
 147                        msleep(10);
 148        }
 149        mutex_unlock(&pv->lock);
 150        return -1;
 151}
 152
 153static void wf_ad7417_release(struct kref *ref)
 154{
 155        struct wf_ad7417_priv *pv = container_of(ref,
 156                                                 struct wf_ad7417_priv, ref);
 157        kfree(pv);
 158}
 159
 160static void wf_ad7417_sensor_release(struct wf_sensor *sr)
 161{
 162        struct wf_ad7417_priv *pv = sr->priv;
 163
 164        kfree(sr->name);
 165        kref_put(&pv->ref, wf_ad7417_release);
 166}
 167
 168static const struct wf_sensor_ops wf_ad7417_temp_ops = {
 169        .get_value      = wf_ad7417_temp_get,
 170        .release        = wf_ad7417_sensor_release,
 171        .owner          = THIS_MODULE,
 172};
 173
 174static const struct wf_sensor_ops wf_ad7417_adc_ops = {
 175        .get_value      = wf_ad7417_adc_get,
 176        .release        = wf_ad7417_sensor_release,
 177        .owner          = THIS_MODULE,
 178};
 179
 180static void __devinit wf_ad7417_add_sensor(struct wf_ad7417_priv *pv,
 181                                           int index, const char *name,
 182                                           const struct wf_sensor_ops *ops)
 183{
 184        pv->sensors[index].name = kasprintf(GFP_KERNEL, "%s-%d", name, pv->cpu);
 185        pv->sensors[index].priv = pv;
 186        pv->sensors[index].ops = ops;
 187        if (!wf_register_sensor(&pv->sensors[index]))
 188                kref_get(&pv->ref);
 189}
 190
 191static void __devinit wf_ad7417_init_chip(struct wf_ad7417_priv *pv)
 192{
 193        int rc;
 194        u8 buf[2];
 195        u8 config = 0;
 196
 197        /*
 198         * Read ADC the configuration register and cache it. We
 199         * also make sure Config2 contains proper values, I've seen
 200         * cases where we got stale grabage in there, thus preventing
 201         * proper reading of conv. values
 202         */
 203
 204        /* Clear Config2 */
 205        buf[0] = 5;
 206        buf[1] = 0;
 207        i2c_master_send(pv->i2c, buf, 2);
 208
 209        /* Read & cache Config1 */
 210        buf[0] = 1;
 211        rc = i2c_master_send(pv->i2c, buf, 1);
 212        if (rc > 0) {
 213                rc = i2c_master_recv(pv->i2c, buf, 1);
 214                if (rc > 0) {
 215                        config = buf[0];
 216
 217                        dev_dbg(&pv->i2c->dev, "ADC config reg: %02x\n",
 218                                config);
 219
 220                        /* Disable shutdown mode */
 221                        config &= 0xfe;
 222                        buf[0] = 1;
 223                        buf[1] = config;
 224                        rc = i2c_master_send(pv->i2c, buf, 2);
 225                }
 226        }
 227        if (rc <= 0)
 228                dev_err(&pv->i2c->dev, "Error reading ADC config\n");
 229
 230        pv->config = config;
 231}
 232
 233static int __devinit wf_ad7417_probe(struct i2c_client *client,
 234                                     const struct i2c_device_id *id)
 235{
 236        struct wf_ad7417_priv *pv;
 237        const struct mpu_data *mpu;
 238        const char *loc;
 239        int cpu_nr;
 240
 241        loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL);
 242        if (!loc) {
 243                dev_warn(&client->dev, "Missing hwsensor-location property!\n");
 244                return -ENXIO;
 245        }
 246
 247        /*
 248         * Identify which CPU we belong to by looking at the first entry
 249         * in the hwsensor-location list
 250         */
 251        if (!strncmp(loc, "CPU A", 5))
 252                cpu_nr = 0;
 253        else if (!strncmp(loc, "CPU B", 5))
 254                cpu_nr = 1;
 255        else {
 256                pr_err("wf_ad7417: Can't identify location %s\n", loc);
 257                return -ENXIO;
 258        }
 259        mpu = wf_get_mpu(cpu_nr);
 260        if (!mpu) {
 261                dev_err(&client->dev, "Failed to retrieve MPU data\n");
 262                return -ENXIO;
 263        }
 264
 265        pv = kzalloc(sizeof(struct wf_ad7417_priv), GFP_KERNEL);
 266        if (pv == NULL)
 267                return -ENODEV;
 268
 269        kref_init(&pv->ref);
 270        mutex_init(&pv->lock);
 271        pv->i2c = client;
 272        pv->cpu = cpu_nr;
 273        pv->mpu = mpu;
 274        dev_set_drvdata(&client->dev, pv);
 275
 276        /* Initialize the chip */
 277        wf_ad7417_init_chip(pv);
 278
 279        /*
 280         * We cannot rely on Apple device-tree giving us child
 281         * node with the names of the individual sensors so we
 282         * just hard code what we know about them
 283         */
 284        wf_ad7417_add_sensor(pv, 0, "cpu-amb-temp", &wf_ad7417_temp_ops);
 285        wf_ad7417_add_sensor(pv, 1, "cpu-diode-temp", &wf_ad7417_adc_ops);
 286        wf_ad7417_add_sensor(pv, 2, "cpu-12v-current", &wf_ad7417_adc_ops);
 287        wf_ad7417_add_sensor(pv, 3, "cpu-voltage", &wf_ad7417_adc_ops);
 288        wf_ad7417_add_sensor(pv, 4, "cpu-current", &wf_ad7417_adc_ops);
 289
 290        return 0;
 291}
 292
 293static int __devexit wf_ad7417_remove(struct i2c_client *client)
 294{
 295        struct wf_ad7417_priv *pv = dev_get_drvdata(&client->dev);
 296        int i;
 297
 298        /* Mark client detached */
 299        pv->i2c = NULL;
 300
 301        /* Release sensor */
 302        for (i = 0; i < 5; i++)
 303                wf_unregister_sensor(&pv->sensors[i]);
 304
 305        kref_put(&pv->ref, wf_ad7417_release);
 306
 307        return 0;
 308}
 309
 310static const struct i2c_device_id wf_ad7417_id[] = {
 311        { "MAC,ad7417", 0 },
 312        { }
 313};
 314MODULE_DEVICE_TABLE(i2c, wf_ad7417_id);
 315
 316static struct i2c_driver wf_ad7417_driver = {
 317        .driver = {
 318                .name   = "wf_ad7417",
 319        },
 320        .probe          = wf_ad7417_probe,
 321        .remove         = wf_ad7417_remove,
 322        .id_table       = wf_ad7417_id,
 323};
 324
 325static int __devinit wf_ad7417_init(void)
 326{
 327        /* This is only supported on these machines */
 328        if (!of_machine_is_compatible("PowerMac7,2") &&
 329            !of_machine_is_compatible("PowerMac7,3") &&
 330            !of_machine_is_compatible("RackMac3,1"))
 331                return -ENODEV;
 332
 333        return i2c_add_driver(&wf_ad7417_driver);
 334}
 335
 336static void __devexit wf_ad7417_exit(void)
 337{
 338        i2c_del_driver(&wf_ad7417_driver);
 339}
 340
 341module_init(wf_ad7417_init);
 342module_exit(wf_ad7417_exit);
 343
 344MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 345MODULE_DESCRIPTION("ad7417 sensor driver for PowerMacs");
 346MODULE_LICENSE("GPL");
 347
 348
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.