Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Windfarm PowerMac thermal control. LM87 sensor
0004  *
0005  * Copyright 2012 Benjamin Herrenschmidt, IBM Corp.
0006  */
0007 
0008 #include <linux/types.h>
0009 #include <linux/errno.h>
0010 #include <linux/kernel.h>
0011 #include <linux/delay.h>
0012 #include <linux/slab.h>
0013 #include <linux/init.h>
0014 #include <linux/wait.h>
0015 #include <linux/i2c.h>
0016 
0017 #include <asm/machdep.h>
0018 #include <asm/io.h>
0019 #include <asm/sections.h>
0020 #include <asm/pmac_low_i2c.h>
0021 
0022 #include "windfarm.h"
0023 
0024 #define VERSION "1.0"
0025 
0026 #undef DEBUG
0027 
0028 #ifdef DEBUG
0029 #define DBG(args...)    printk(args)
0030 #else
0031 #define DBG(args...)    do { } while(0)
0032 #endif
0033 
0034 struct wf_lm87_sensor {
0035     struct i2c_client   *i2c;
0036     struct wf_sensor    sens;
0037 };
0038 #define wf_to_lm87(c) container_of(c, struct wf_lm87_sensor, sens)
0039 
0040 
0041 static int wf_lm87_read_reg(struct i2c_client *chip, int reg)
0042 {
0043     int rc, tries = 0;
0044     u8 buf;
0045 
0046     for (;;) {
0047         /* Set address */
0048         buf = (u8)reg;
0049         rc = i2c_master_send(chip, &buf, 1);
0050         if (rc <= 0)
0051             goto error;
0052         rc = i2c_master_recv(chip, &buf, 1);
0053         if (rc <= 0)
0054             goto error;
0055         return (int)buf;
0056     error:
0057         DBG("wf_lm87: Error reading LM87, retrying...\n");
0058         if (++tries > 10) {
0059             printk(KERN_ERR "wf_lm87: Error reading LM87 !\n");
0060             return -EIO;
0061         }
0062         msleep(10);
0063     }
0064 }
0065 
0066 static int wf_lm87_get(struct wf_sensor *sr, s32 *value)
0067 {
0068     struct wf_lm87_sensor *lm = sr->priv;
0069     s32 temp;
0070 
0071     if (lm->i2c == NULL)
0072         return -ENODEV;
0073 
0074 #define LM87_INT_TEMP       0x27
0075 
0076     /* Read temperature register */
0077     temp = wf_lm87_read_reg(lm->i2c, LM87_INT_TEMP);
0078     if (temp < 0)
0079         return temp;
0080     *value = temp << 16;
0081 
0082     return 0;
0083 }
0084 
0085 static void wf_lm87_release(struct wf_sensor *sr)
0086 {
0087     struct wf_lm87_sensor *lm = wf_to_lm87(sr);
0088 
0089     kfree(lm);
0090 }
0091 
0092 static const struct wf_sensor_ops wf_lm87_ops = {
0093     .get_value  = wf_lm87_get,
0094     .release    = wf_lm87_release,
0095     .owner      = THIS_MODULE,
0096 };
0097 
0098 static int wf_lm87_probe(struct i2c_client *client,
0099              const struct i2c_device_id *id)
0100 {   
0101     struct wf_lm87_sensor *lm;
0102     const char *name = NULL, *loc;
0103     struct device_node *np = NULL;
0104     int rc;
0105 
0106     /*
0107      * The lm87 contains a whole pile of sensors, additionally,
0108      * the Xserve G5 has several lm87's. However, for now we only
0109      * care about the internal temperature sensor
0110      */
0111     for_each_child_of_node(client->dev.of_node, np) {
0112         if (!of_node_name_eq(np, "int-temp"))
0113             continue;
0114         loc = of_get_property(np, "location", NULL);
0115         if (!loc)
0116             continue;
0117         if (strstr(loc, "DIMM"))
0118             name = "dimms-temp";
0119         else if (strstr(loc, "Processors"))
0120             name = "between-cpus-temp";
0121         if (name) {
0122             of_node_put(np);
0123             break;
0124         }
0125     }
0126     if (!name) {
0127         pr_warn("wf_lm87: Unsupported sensor %pOF\n",
0128             client->dev.of_node);
0129         return -ENODEV;
0130     }
0131 
0132     lm = kzalloc(sizeof(struct wf_lm87_sensor), GFP_KERNEL);
0133     if (lm == NULL)
0134         return -ENODEV;
0135 
0136     lm->i2c = client;
0137     lm->sens.name = name;
0138     lm->sens.ops = &wf_lm87_ops;
0139     lm->sens.priv = lm;
0140     i2c_set_clientdata(client, lm);
0141 
0142     rc = wf_register_sensor(&lm->sens);
0143     if (rc)
0144         kfree(lm);
0145     return rc;
0146 }
0147 
0148 static int wf_lm87_remove(struct i2c_client *client)
0149 {
0150     struct wf_lm87_sensor *lm = i2c_get_clientdata(client);
0151 
0152     /* Mark client detached */
0153     lm->i2c = NULL;
0154 
0155     /* release sensor */
0156     wf_unregister_sensor(&lm->sens);
0157 
0158     return 0;
0159 }
0160 
0161 static const struct i2c_device_id wf_lm87_id[] = {
0162     { "MAC,lm87cimt", 0 },
0163     { }
0164 };
0165 MODULE_DEVICE_TABLE(i2c, wf_lm87_id);
0166 
0167 static const struct of_device_id wf_lm87_of_id[] = {
0168     { .compatible = "lm87cimt", },
0169     { }
0170 };
0171 MODULE_DEVICE_TABLE(of, wf_lm87_of_id);
0172 
0173 static struct i2c_driver wf_lm87_driver = {
0174     .driver = {
0175         .name   = "wf_lm87",
0176         .of_match_table = wf_lm87_of_id,
0177     },
0178     .probe      = wf_lm87_probe,
0179     .remove     = wf_lm87_remove,
0180     .id_table   = wf_lm87_id,
0181 };
0182 
0183 static int __init wf_lm87_sensor_init(void)
0184 {
0185     /* We only support this on the Xserve */
0186     if (!of_machine_is_compatible("RackMac3,1"))
0187         return -ENODEV;
0188 
0189     return i2c_add_driver(&wf_lm87_driver);
0190 }
0191 
0192 static void __exit wf_lm87_sensor_exit(void)
0193 {
0194     i2c_del_driver(&wf_lm87_driver);
0195 }
0196 
0197 
0198 module_init(wf_lm87_sensor_init);
0199 module_exit(wf_lm87_sensor_exit);
0200 
0201 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
0202 MODULE_DESCRIPTION("LM87 sensor objects for PowerMacs thermal control");
0203 MODULE_LICENSE("GPL");
0204