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