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 "0.2"
  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        int                     nr;
  38        atomic_t                refcnt;
  39        struct mutex            mutex;
  40        unsigned long           last_read; /* jiffies when cache last updated */
  41        u8                      cache[16];
  42        struct i2c_client       *i2c;
  43        struct device_node      *node;
  44};
  45
  46static struct wf_sat *sats[2];
  47
  48struct wf_sat_sensor {
  49        int             index;
  50        int             index2;         /* used for power sensors */
  51        int             shift;
  52        struct wf_sat   *sat;
  53        struct wf_sensor sens;
  54};
  55
  56#define wf_to_sat(c)    container_of(c, struct wf_sat_sensor, sens)
  57
  58struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id,
  59                                                  unsigned int *size)
  60{
  61        struct wf_sat *sat;
  62        int err;
  63        unsigned int i, len;
  64        u8 *buf;
  65        u8 data[4];
  66
  67        /* TODO: Add the resulting partition to the device-tree */
  68
  69        if (sat_id > 1 || (sat = sats[sat_id]) == NULL)
  70                return NULL;
  71
  72        err = i2c_smbus_write_word_data(sat->i2c, 8, id << 8);
  73        if (err) {
  74                printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err);
  75                return NULL;
  76        }
  77
  78        err = i2c_smbus_read_word_data(sat->i2c, 9);
  79        if (err < 0) {
  80                printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n");
  81                return NULL;
  82        }
  83        len = err;
  84        if (len == 0) {
  85                printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id);
  86                return NULL;
  87        }
  88
  89        len = le16_to_cpu(len);
  90        len = (len + 3) & ~3;
  91        buf = kmalloc(len, GFP_KERNEL);
  92        if (buf == NULL)
  93                return NULL;
  94
  95        for (i = 0; i < len; i += 4) {
  96                err = i2c_smbus_read_i2c_block_data(sat->i2c, 0xa, 4, data);
  97                if (err < 0) {
  98                        printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n",
  99                               err);
 100                        goto fail;
 101                }
 102                buf[i] = data[1];
 103                buf[i+1] = data[0];
 104                buf[i+2] = data[3];
 105                buf[i+3] = data[2];
 106        }
 107#ifdef DEBUG
 108        DBG(KERN_DEBUG "sat %d partition %x:", sat_id, id);
 109        for (i = 0; i < len; ++i)
 110                DBG(" %x", buf[i]);
 111        DBG("\n");
 112#endif
 113
 114        if (size)
 115                *size = len;
 116        return (struct smu_sdbp_header *) buf;
 117
 118 fail:
 119        kfree(buf);
 120        return NULL;
 121}
 122EXPORT_SYMBOL_GPL(smu_sat_get_sdb_partition);
 123
 124/* refresh the cache */
 125static int wf_sat_read_cache(struct wf_sat *sat)
 126{
 127        int err;
 128
 129        err = i2c_smbus_read_i2c_block_data(sat->i2c, 0x3f, 16, sat->cache);
 130        if (err < 0)
 131                return err;
 132        sat->last_read = jiffies;
 133#ifdef LOTSA_DEBUG
 134        {
 135                int i;
 136                DBG(KERN_DEBUG "wf_sat_get: data is");
 137                for (i = 0; i < 16; ++i)
 138                        DBG(" %.2x", sat->cache[i]);
 139                DBG("\n");
 140        }
 141#endif
 142        return 0;
 143}
 144
 145static int wf_sat_get(struct wf_sensor *sr, s32 *value)
 146{
 147        struct wf_sat_sensor *sens = wf_to_sat(sr);
 148        struct wf_sat *sat = sens->sat;
 149        int i, err;
 150        s32 val;
 151
 152        if (sat->i2c == NULL)
 153                return -ENODEV;
 154
 155        mutex_lock(&sat->mutex);
 156        if (time_after(jiffies, (sat->last_read + MAX_AGE))) {
 157                err = wf_sat_read_cache(sat);
 158                if (err)
 159                        goto fail;
 160        }
 161
 162        i = sens->index * 2;
 163        val = ((sat->cache[i] << 8) + sat->cache[i+1]) << sens->shift;
 164        if (sens->index2 >= 0) {
 165                i = sens->index2 * 2;
 166                /* 4.12 * 8.8 -> 12.20; shift right 4 to get 16.16 */
 167                val = (val * ((sat->cache[i] << 8) + sat->cache[i+1])) >> 4;
 168        }
 169
 170        *value = val;
 171        err = 0;
 172
 173 fail:
 174        mutex_unlock(&sat->mutex);
 175        return err;
 176}
 177
 178static void wf_sat_release(struct wf_sensor *sr)
 179{
 180        struct wf_sat_sensor *sens = wf_to_sat(sr);
 181        struct wf_sat *sat = sens->sat;
 182
 183        if (atomic_dec_and_test(&sat->refcnt)) {
 184                if (sat->nr >= 0)
 185                        sats[sat->nr] = NULL;
 186                kfree(sat);
 187        }
 188        kfree(sens);
 189}
 190
 191static struct wf_sensor_ops wf_sat_ops = {
 192        .get_value      = wf_sat_get,
 193        .release        = wf_sat_release,
 194        .owner          = THIS_MODULE,
 195};
 196
 197static struct i2c_driver wf_sat_driver;
 198
 199static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev)
 200{
 201        struct i2c_board_info info;
 202        struct i2c_client *client;
 203        const u32 *reg;
 204        u8 addr;
 205
 206        reg = of_get_property(dev, "reg", NULL);
 207        if (reg == NULL)
 208                return;
 209        addr = *reg;
 210        DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr);
 211
 212        memset(&info, 0, sizeof(struct i2c_board_info));
 213        info.addr = (addr >> 1) & 0x7f;
 214        info.platform_data = dev;
 215        strlcpy(info.type, "wf_sat", I2C_NAME_SIZE);
 216
 217        client = i2c_new_device(adapter, &info);
 218        if (client == NULL) {
 219                printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n");
 220                return;
 221        }
 222
 223        /*
 224         * Let i2c-core delete that device on driver removal.
 225         * This is safe because i2c-core holds the core_lock mutex for us.
 226         */
 227        list_add_tail(&client->detected, &wf_sat_driver.clients);
 228}
 229
 230static int wf_sat_probe(struct i2c_client *client,
 231                        const struct i2c_device_id *id)
 232{
 233        struct device_node *dev = client->dev.platform_data;
 234        struct wf_sat *sat;
 235        struct wf_sat_sensor *sens;
 236        const u32 *reg;
 237        const char *loc, *type;
 238        u8 chip, core;
 239        struct device_node *child;
 240        int shift, cpu, index;
 241        char *name;
 242        int vsens[2], isens[2];
 243
 244        sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL);
 245        if (sat == NULL)
 246                return -ENOMEM;
 247        sat->nr = -1;
 248        sat->node = of_node_get(dev);
 249        atomic_set(&sat->refcnt, 0);
 250        mutex_init(&sat->mutex);
 251        sat->i2c = client;
 252        i2c_set_clientdata(client, sat);
 253
 254        vsens[0] = vsens[1] = -1;
 255        isens[0] = isens[1] = -1;
 256        child = NULL;
 257        while ((child = of_get_next_child(dev, child)) != NULL) {
 258                reg = of_get_property(child, "reg", NULL);
 259                type = of_get_property(child, "device_type", NULL);
 260                loc = of_get_property(child, "location", NULL);
 261                if (reg == NULL || loc == NULL)
 262                        continue;
 263
 264                /* the cooked sensors are between 0x30 and 0x37 */
 265                if (*reg < 0x30 || *reg > 0x37)
 266                        continue;
 267                index = *reg - 0x30;
 268
 269                /* expect location to be CPU [AB][01] ... */
 270                if (strncmp(loc, "CPU ", 4) != 0)
 271                        continue;
 272                chip = loc[4] - 'A';
 273                core = loc[5] - '0';
 274                if (chip > 1 || core > 1) {
 275                        printk(KERN_ERR "wf_sat_create: don't understand "
 276                               "location %s for %s\n", loc, child->full_name);
 277                        continue;
 278                }
 279                cpu = 2 * chip + core;
 280                if (sat->nr < 0)
 281                        sat->nr = chip;
 282                else if (sat->nr != chip) {
 283                        printk(KERN_ERR "wf_sat_create: can't cope with "
 284                               "multiple CPU chips on one SAT (%s)\n", loc);
 285                        continue;
 286                }
 287
 288                if (strcmp(type, "voltage-sensor") == 0) {
 289                        name = "cpu-voltage";
 290                        shift = 4;
 291                        vsens[core] = index;
 292                } else if (strcmp(type, "current-sensor") == 0) {
 293                        name = "cpu-current";
 294                        shift = 8;
 295                        isens[core] = index;
 296                } else if (strcmp(type, "temp-sensor") == 0) {
 297                        name = "cpu-temp";
 298                        shift = 10;
 299                } else
 300                        continue;       /* hmmm shouldn't happen */
 301
 302                /* the +16 is enough for "cpu-voltage-n" */
 303                sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);
 304                if (sens == NULL) {
 305                        printk(KERN_ERR "wf_sat_create: couldn't create "
 306                               "%s sensor %d (no memory)\n", name, cpu);
 307                        continue;
 308                }
 309                sens->index = index;
 310                sens->index2 = -1;
 311                sens->shift = shift;
 312                sens->sat = sat;
 313                atomic_inc(&sat->refcnt);
 314                sens->sens.ops = &wf_sat_ops;
 315                sens->sens.name = (char *) (sens + 1);
 316                snprintf(sens->sens.name, 16, "%s-%d", name, cpu);
 317
 318                if (wf_register_sensor(&sens->sens)) {
 319                        atomic_dec(&sat->refcnt);
 320                        kfree(sens);
 321                }
 322        }
 323
 324        /* make the power sensors */
 325        for (core = 0; core < 2; ++core) {
 326                if (vsens[core] < 0 || isens[core] < 0)
 327                        continue;
 328                cpu = 2 * sat->nr + core;
 329                sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL);
 330                if (sens == NULL) {
 331                        printk(KERN_ERR "wf_sat_create: couldn't create power "
 332                               "sensor %d (no memory)\n", cpu);
 333                        continue;
 334                }
 335                sens->index = vsens[core];
 336                sens->index2 = isens[core];
 337                sens->shift = 0;
 338                sens->sat = sat;
 339                atomic_inc(&sat->refcnt);
 340                sens->sens.ops = &wf_sat_ops;
 341                sens->sens.name = (char *) (sens + 1);
 342                snprintf(sens->sens.name, 16, "cpu-power-%d", cpu);
 343
 344                if (wf_register_sensor(&sens->sens)) {
 345                        atomic_dec(&sat->refcnt);
 346                        kfree(sens);
 347                }
 348        }
 349
 350        if (sat->nr >= 0)
 351                sats[sat->nr] = sat;
 352
 353        return 0;
 354}
 355
 356static int wf_sat_attach(struct i2c_adapter *adapter)
 357{
 358        struct device_node *busnode, *dev = NULL;
 359        struct pmac_i2c_bus *bus;
 360
 361        bus = pmac_i2c_adapter_to_bus(adapter);
 362        if (bus == NULL)
 363                return -ENODEV;
 364        busnode = pmac_i2c_get_bus_node(bus);
 365
 366        while ((dev = of_get_next_child(busnode, dev)) != NULL)
 367                if (of_device_is_compatible(dev, "smu-sat"))
 368                        wf_sat_create(adapter, dev);
 369        return 0;
 370}
 371
 372static int wf_sat_remove(struct i2c_client *client)
 373{
 374        struct wf_sat *sat = i2c_get_clientdata(client);
 375
 376        /* XXX TODO */
 377
 378        sat->i2c = NULL;
 379        return 0;
 380}
 381
 382static const struct i2c_device_id wf_sat_id[] = {
 383        { "wf_sat", 0 },
 384        { }
 385};
 386
 387static struct i2c_driver wf_sat_driver = {
 388        .driver = {
 389                .name           = "wf_smu_sat",
 390        },
 391        .attach_adapter = wf_sat_attach,
 392        .probe          = wf_sat_probe,
 393        .remove         = wf_sat_remove,
 394        .id_table       = wf_sat_id,
 395};
 396
 397static int __init sat_sensors_init(void)
 398{
 399        return i2c_add_driver(&wf_sat_driver);
 400}
 401
 402#if 0   /* uncomment when module_exit() below is uncommented */
 403static void __exit sat_sensors_exit(void)
 404{
 405        i2c_del_driver(&wf_sat_driver);
 406}
 407#endif
 408
 409module_init(sat_sensors_init);
 410/*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */
 411
 412MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
 413MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control");
 414MODULE_LICENSE("GPL");
 415