linux/drivers/macintosh/windfarm_smu_sat.c
<<
>>
Prefs
   1/*
   2 * Windfarm PowerMac thermal control.  SMU "satellite" controller sensors.
   3 *
   4 * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>
   5 *
   6 * Released under the terms 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/slab.h>
  13#include <linux/init.h>
  14#include <linux/wait.h>
  15#include <linux/i2c.h>
  16#include <linux/mutex.h>
  17#include <asm/prom.h>
  18#include <asm/smu.h>
  19#include <asm/pmac_low_i2c.h>
  20
  21#include "windfarm.h"
  22
  23#define VERSION "1.0"
  24
  25#define DEBUG
  26
  27#ifdef DEBUG
  28#define DBG(args...)    printk(args)
  29#else
  30#define DBG(args...)    do { } while(0)
  31#endif
  32
  33/* If the cache is older than 800ms we'll refetch it */
  34#define MAX_AGE         msecs_to_jiffies(800)
  35
  36struct wf_sat {
  37        struct kref             ref;
  38        int                     nr;
  39        struct mutex            mutex;
  40        unsigned long           last_read; /* jiffies when cache last updated */
  41        u8                      cache[16];
  42        struct list_head        sensors;
  43        struct i2c_client       *i2c;
  44        struct device_node      *node;
  45};
  46
  47static struct wf_sat *sats[2];
  48
  49struct wf_sat_sensor {
  50        struct list_head        link;
  51        int                     index;
  52        int                     index2;         /* used for power sensors */
  53        int                     shift;
  54        struct wf_sat           *sat;
  55        struct wf_sensor        sens;
  56};
  57
  58#define wf_to_sat(c)    container_of(c, struct wf_sat_sensor, sens)
  59
  60struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
  61                                                  unsigned int *size)
  62{
  63        struct wf_sat *sat;
  64        int err;
  65        unsigned int i, len;
  66        u8 *buf;
  67        u8 data[4];
  68
  69        /* TODO: Add the resulting partition to the device-tree */
  70
  71        if (sat_id > 1 || (sat = sats[sat_id]) == NULL)
  72                return NULL;
  73
  74        err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8);
  75        if (err) {
  76                printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err);
  77                return NULL;
  78        }
  79
  80        err = i2c_smbus_read_word_data(sat->i2c, 9);
  81        if (err < 0) {
  82                printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n");
  83                return NULL;
  84        }
  85        len = err;
  86        if (len == 0) {
  87                printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id);
  88                return NULL;
  89        }
  90
  91        len = le16_to_cpu(len);
  92        len = (len + 3) & ~3;
  93        buf = kmalloc(len, GFP_KERNEL);
  94        if (buf == NULL)
  95                return NULL;
  96
  97        for (i = 0; i < len; i += 4) {
  98                err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data);
  99                if (err < 0) {
 100                        printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
 101                               err);
 102                        goto fail;
 103                }
 104                buf[i] = data[1];
 105                buf[i+1] = data[0];
 106                buf[i+2] = data[3];
 107                buf[i+3] = data[2];
 108        }
 109#ifdef DEBUG
 110        DBG(KERN_DEBUG "sat %d partition %x:", sat_id, id);
 111        for (i = 0; i < len; ++i)
 112                DBG(" %x", buf[i]);
 113        DBG("\n");
 114#endif
 115
 116        if (size)
 117                *size = len;
 118        return (struct smu_sdbp_header *) buf;
 119
 120 fail:
 121        kfree(buf);
 122        return NULL;
 123}
 124EXPORT_SYMBOL_GPL(smu_sat_get_sdb_partition);
 125
 126/* refresh the cache */
 127static int wf_sat_read_cache(struct wf_sat *sat)
 128{
 129        int err;
 130
 131        err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache);
 132        if (err < 0)
 133                return err;
 134        sat->last_read = jiffies;
 135#ifdef LOTSA_DEBUG
 136        {
 137                int i;
 138                DBG(KERN_DEBUG "wf_sat_get: data is");
 139                for (i = 0; i < 16; ++i)
 140                        DBG(" %.2x", sat->cache[i]);
 141                DBG("\n");
 142        }
 143#endif
 144        return 0;
 145}
 146
 147static int wf_sat_sensor_get(struct wf_sensor *sr, s32 *value)
 148{
 149        struct wf_sat_sensor *sens = wf_to_sat(sr);
 150        struct wf_sat *sat = sens->sat;
 151        int i, err;
 152        s32 val;
 153
 154        if (sat->i2c == NULL)
 155                return -ENODEV;
 156
 157        mutex_lock(&sat->mutex);
 158        if (time_after(jiffies, (sat->last_read + MAX_AGE))) {
 159                err = wf_sat_read_cache(sat);
 160                if (err)
 161                        goto fail;
 162        }
 163
 164        i = sens->index * 2;
 165        val = ((sat->cache[i] << 8) + sat->cache[i+1]) << sens->shift;
 166        if (sens->index2 >= 0) {
 167                i = sens->index2 * 2;
 168                /* 4.12 * 8.8 -> 12.20; shift right 4 to get 16.16 */
 169                val = (val * ((sat->cache[i] << 8) + sat->cache[i+1])) >> 4;
 170        }
 171
 172        *value = val;
 173        err = 0;
 174
 175 fail:
 176        mutex_unlock(&sat->mutex);
 177        return err;
 178}
 179
 180static void wf_sat_release(struct kref *ref)
 181{
 182        struct wf_sat *sat = container_of(ref, struct wf_sat, ref);
 183
 184        if (sat->nr >= 0)
 185                sats[sat->nr] = NULL;
 186        kfree(sat);
 187}
 188
 189static void wf_sat_sensor_release(struct wf_sensor *sr)
 190{
 191        struct wf_sat_sensor *sens = wf_to_sat(sr);
 192        struct wf_sat *sat = sens->sat;
 193
 194        kfree(sens);
 195        kref_put(&sat->ref, wf_sat_release);
 196}
 197
 198static struct wf_sensor_ops wf_sat_ops = {
 199        .get_value      = wf_sat_sensor_get,
 200        .release        = wf_sat_sensor_release,
 201        .owner          = THIS_MODULE,
 202};
 203
 204static int wf_sat_probe(struct i2c_client *client,
 205                        const struct i2c_device_id *id)
 206{
 207        struct device_node *dev = client->dev.of_node;
 208        struct wf_sat *sat;
 209        struct wf_sat_sensor *sens;
 210        const u32 *reg;
 211        const char *loc, *type;
 212        u8 chip, core;
 213        struct device_node *child;
 214        int shift, cpu, index;
 215        char *name;
 216        int vsens[2], isens[2];
 217
 218        sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL);
 219        if (sat == NULL)
 220                return -ENOMEM;
 221        sat->nr = -1;
 222        sat->node = of_node_get(dev);
 223        kref_init(&sat->ref);
 224        mutex_init(&sat->mutex);
 225        sat->i2c = client;
 226        INIT_LIST_HEAD(&sat->sensors);
 227        i2c_set_clientdata(client, sat);
 228
 229        vsens[0] = vsens[1] = -1;
 230        isens[0] = isens[1] = -1;
 231        child = NULL;
 232        while ((child = of_get_next_child(dev, child)) != NULL) {
 233                reg = of_get_property(child, "reg", NULL);
 234                type = of_get_property(child, "device_type", NULL);
 235                loc = of_get_property(child, "location", NULL);
 236                if (reg == NULL || loc == NULL)
 237                        continue;
 238
 239                /* the cooked sensors are between 0x30 and 0x37 */
 240                if (*reg < 0x30 || *reg > 0x37)
 241                        continue;
 242                index = *reg - 0x30;
 243
 244                /* expect location to be CPU [AB][01] ... */
 245                if (strncmp(loc, "CPU ", 4) != 0)
 246                        continue;
 247                chip = loc[4] - 'A';
 248                core = loc[5] - '0';
 249                if (chip > 1 || core > 1) {
 250                        printk(KERN_ERR "wf_sat_create: don't understand "
 251                               "location %s for %s\n", loc, child->full_name);
 252                        continue;
 253                }
 254                cpu = 2 * chip + core;
 255                if (sat->nr < 0)
 256                        sat->nr = chip;
 257                else if (sat->nr != chip) {
 258                        printk(KERN_ERR "wf_sat_create: can't cope with "
 259                               "multiple CPU chips on one SAT (%s)\n", loc);
 260                        continue;
 261                }
 262
 263                if (strcmp(type, "voltage-sensor") == 0) {
 264                        name = "cpu-voltage";
 265                        shift = 4;
 266                        vsens[core] = index;
 267                } else if (strcmp(type, "current-sensor") == 0) {
 268                        name = "cpu-current";
 269                        shift = 8;
 270                        isens[core] = index;
 271                } else if (strcmp(type, "temp-sensor") == 0) {
 272                        name = "cpu-temp";
 273                        shift = 10;
 274                } else
 275                        continue;       /* hmmm shouldn't happen */
 276
 277                /* the +16 is enough for "cpu-voltage-n" */
 278                sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);
 279                if (sens == NULL) {
 280                        printk(KERN_ERR "wf_sat_create: couldn't create "
 281                               "%s sensor %d (no memory)\n", name, cpu);
 282                        continue;
 283                }
 284                sens->index = index;
 285                sens->index2 = -1;
 286                sens->shift = shift;
 287                sens->sat = sat;
 288                sens->sens.ops = &wf_sat_ops;
 289                sens->sens.name = (char *) (sens + 1);
 290                snprintf((char *)sens->sens.name, 16, "%s-%d", name, cpu);
 291
 292                if (wf_register_sensor(&sens->sens))
 293                        kfree(sens);
 294                else {
 295                        list_add(&sens->link, &sat->sensors);
 296                        kref_get(&sat->ref);
 297                }
 298        }
 299
 300        /* make the power sensors */
 301        for (core = 0; core < 2; ++core) {
 302                if (vsens[core] < 0 || isens[core] < 0)
 303                        continue;
 304                cpu = 2 * sat->nr + core;
 305                sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);
 306                if (sens == NULL) {
 307                        printk(KERN_ERR "wf_sat_create: couldn't create power "
 308                               "sensor %d (no memory)\n", cpu);
 309                        continue;
 310                }
 311                sens->index = vsens[core];
 312                sens->index2 = isens[core];
 313                sens->shift = 0;
 314                sens->sat = sat;
 315                sens->sens.ops = &wf_sat_ops;
 316                sens->sens.name = (char *) (sens + 1);
 317                snprintf((char *)sens->sens.name, 16, "cpu-power-%d", cpu);
 318
 319                if (wf_register_sensor(&sens->sens))
 320                        kfree(sens);
 321                else {
 322                        list_add(&sens->link, &sat->sensors);
 323                        kref_get(&sat->ref);
 324                }
 325        }
 326
 327        if (sat->nr >= 0)
 328                sats[sat->nr] = sat;
 329
 330        return 0;
 331}
 332
 333static int wf_sat_remove(struct i2c_client *client)
 334{
 335        struct wf_sat *sat = i2c_get_clientdata(client);
 336        struct wf_sat_sensor *sens;
 337
 338        /* release sensors */
 339        while(!list_empty(&sat->sensors)) {
 340                sens = list_first_entry(&sat->sensors,
 341                                        struct wf_sat_sensor, link);
 342                list_del(&sens->link);
 343                wf_unregister_sensor(&sens->sens);
 344        }
 345        sat->i2c = NULL;
 346        i2c_set_clientdata(client, NULL);
 347        kref_put(&sat->ref, wf_sat_release);
 348
 349        return 0;
 350}
 351
 352static const struct i2c_device_id wf_sat_id[] = {
 353        { "MAC,smu-sat", 0 },
 354        { }
 355};
 356MODULE_DEVICE_TABLE(i2c, wf_sat_id);
 357
 358static struct i2c_driver wf_sat_driver = {
 359        .driver = {
 360                .name           = "wf_smu_sat",
 361        },
 362        .probe          = wf_sat_probe,
 363        .remove         = wf_sat_remove,
 364        .id_table       = wf_sat_id,
 365};
 366
 367module_i2c_driver(wf_sat_driver);
 368
 369MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
 370MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control");
 371MODULE_LICENSE("GPL");
 372
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.