linux/drivers/macintosh/windfarm_lm87_sensor.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Windfarm PowerMac thermal control. LM87 sensor
   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#include <asm/pmac_low_i2c.h>
  21
  22#include "windfarm.h"
  23
  24#define VERSION "1.0"
  25
  26#undef DEBUG
  27
  28#ifdef DEBUG
  29#define DBG(args...)    printk(args)
  30#else
  31#define DBG(args...)    do { } while(0)
  32#endif
  33
  34struct wf_lm87_sensor {
  35        struct i2c_client       *i2c;
  36        struct wf_sensor        sens;
  37};
  38#define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens)
  39
  40
  41static int wf_lm87_read_reg(struct i2c_client *chip, int reg)
  42{
  43        int rc, tries = 0;
  44        u8 buf;
  45
  46        for (;;) {
  47                /* Set address */
  48                buf = (u8)reg;
  49                rc = i2c_master_send(chip, &buf, 1);
  50                if (rc <= 0)
  51                        goto error;
  52                rc = i2c_master_recv(chip, &buf, 1);
  53                if (rc <= 0)
  54                        goto error;
  55                return (int)buf;
  56        error:
  57                DBG("wf_lm87: Error reading LM87, retrying...\n");
  58                if (++tries > 10) {
  59                        printk(KERN_ERR "wf_lm87: Error reading LM87 !\n");
  60                        return -EIO;
  61                }
  62                msleep(10);
  63        }
  64}
  65
  66static int wf_lm87_get(struct wf_sensor *sr, s32 *value)
  67{
  68        struct wf_lm87_sensor *lm = sr->priv;
  69        s32 temp;
  70
  71        if (lm->i2c == NULL)
  72                return -ENODEV;
  73
  74#define LM87_INT_TEMP           0x27
  75
  76        /* Read temperature register */
  77        temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP);
  78        if (temp < 0)
  79                return temp;
  80        *value = temp << 16;
  81
  82        return 0;
  83}
  84
  85static void wf_lm87_release(struct wf_sensor *sr)
  86{
  87        struct wf_lm87_sensor *lm = wf_to_lm87(sr);
  88
  89        kfree(lm);
  90}
  91
  92static const struct wf_sensor_ops wf_lm87_ops = {
  93        .get_value      = wf_lm87_get,
  94        .release        = wf_lm87_release,
  95        .owner          = THIS_MODULE,
  96};
  97
  98static int wf_lm87_probe(struct i2c_client *client,
  99                         const struct i2c_device_id *id)
 100{       
 101        struct wf_lm87_sensor *lm;
 102        const char *name = NULL, *loc;
 103        struct device_node *np = NULL;
 104        int rc;
 105
 106        /*
 107         * The lm87 contains a whole pile of sensors, additionally,
 108         * the Xserve G5 has several lm87's. However, for now we only
 109         * care about the internal temperature sensor
 110         */
 111        for_each_child_of_node(client->dev.of_node, np) {
 112                if (!of_node_name_eq(np, "int-temp"))
 113                        continue;
 114                loc = of_get_property(np, "location", NULL);
 115                if (!loc)
 116                        continue;
 117                if (strstr(loc, "DIMM"))
 118                        name = "dimms-temp";
 119                else if (strstr(loc, "Processors"))
 120                        name = "between-cpus-temp";
 121                if (name) {
 122                        of_node_put(np);
 123                        break;
 124                }
 125        }
 126        if (!name) {
 127                pr_warn("wf_lm87: Unsupported sensor %pOF\n",
 128                        client->dev.of_node);
 129                return -ENODEV;
 130        }
 131
 132        lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL);
 133        if (lm == NULL)
 134                return -ENODEV;
 135
 136        lm->i2c = client;
 137        lm->sens.name = name;
 138        lm->sens.ops = &wf_lm87_ops;
 139        lm->sens.priv = lm;
 140        i2c_set_clientdata(client, lm);
 141
 142        rc = wf_register_sensor(&lm->sens);
 143        if (rc)
 144                kfree(lm);
 145        return rc;
 146}
 147
 148static int wf_lm87_remove(struct i2c_client *client)
 149{
 150        struct wf_lm87_sensor *lm = i2c_get_clientdata(client);
 151
 152        /* Mark client detached */
 153        lm->i2c = NULL;
 154
 155        /* release sensor */
 156        wf_unregister_sensor(&lm->sens);
 157
 158        return 0;
 159}
 160
 161static const struct i2c_device_id wf_lm87_id[] = {
 162        { "MAC,lm87cimt", 0 },
 163        { }
 164};
 165MODULE_DEVICE_TABLE(i2c, wf_lm87_id);
 166
 167static const struct of_device_id wf_lm87_of_id[] = {
 168        { .compatible = "lm87cimt", },
 169        { }
 170};
 171MODULE_DEVICE_TABLE(of, wf_lm87_of_id);
 172
 173static struct i2c_driver wf_lm87_driver = {
 174        .driver = {
 175                .name   = "wf_lm87",
 176                .of_match_table = wf_lm87_of_id,
 177        },
 178        .probe          = wf_lm87_probe,
 179        .remove         = wf_lm87_remove,
 180        .id_table       = wf_lm87_id,
 181};
 182
 183static int __init wf_lm87_sensor_init(void)
 184{
 185        /* We only support this on the Xserve */
 186        if (!of_machine_is_compatible("RackMac3,1"))
 187                return -ENODEV;
 188
 189        return i2c_add_driver(&wf_lm87_driver);
 190}
 191
 192static void __exit wf_lm87_sensor_exit(void)
 193{
 194        i2c_del_driver(&wf_lm87_driver);
 195}
 196
 197
 198module_init(wf_lm87_sensor_init);
 199module_exit(wf_lm87_sensor_exit);
 200
 201MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 202MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control");
 203MODULE_LICENSE("GPL");
 204
 205