Commit 08fe5bc75f8c43042963dee4f33c9c6cb7791b69

Authored by Benjamin Herrenschmidt
1 parent 850dc32168

powerpc/windfarm: Add lm87 sensor

For use by the upcoming windfarm_rm31

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Showing 1 changed file with 201 additions and 0 deletions Side-by-side Diff

drivers/macintosh/windfarm_lm87_sensor.c
  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 +
  36 +struct 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 +
  43 +static 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 +
  68 +static 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 +
  87 +static 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 +
  94 +static struct wf_sensor_ops wf_lm87_ops = {
  95 + .get_value = wf_lm87_get,
  96 + .release = wf_lm87_release,
  97 + .owner = THIS_MODULE,
  98 +};
  99 +
  100 +static 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 +
  150 +static 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 +
  165 +static const struct i2c_device_id wf_lm87_id[] = {
  166 + { "MAC,lm87cimt", 0 },
  167 + { }
  168 +};
  169 +MODULE_DEVICE_TABLE(i2c, wf_lm87_id);
  170 +
  171 +static 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 +
  180 +static 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 +
  189 +static void __exit wf_lm87_sensor_exit(void)
  190 +{
  191 + i2c_del_driver(&wf_lm87_driver);
  192 +}
  193 +
  194 +
  195 +module_init(wf_lm87_sensor_init);
  196 +module_exit(wf_lm87_sensor_exit);
  197 +
  198 +MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
  199 +MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control");
  200 +MODULE_LICENSE("GPL");