1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Windfarm PowerMac thermal control. MAX6690 sensor. 4 * 5 * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org> 6 */ 7 #include <linux/types.h> 8 #include <linux/errno.h> 9 #include <linux/kernel.h> 10 #include <linux/init.h> 11 #include <linux/slab.h> 12 #include <linux/i2c.h> 13 14 #include <asm/pmac_low_i2c.h> 15 16 #include "windfarm.h" 17 18 #define VERSION "1.0" 19 20 /* This currently only exports the external temperature sensor, 21 since that's all the control loops need. */ 22 23 /* Some MAX6690 register numbers */ 24 #define MAX6690_INTERNAL_TEMP 0 25 #define MAX6690_EXTERNAL_TEMP 1 26 27 struct wf_6690_sensor { 28 struct i2c_client *i2c; 29 struct wf_sensor sens; 30 }; 31 32 #define wf_to_6690(x) container_of((x), struct wf_6690_sensor, sens) 33 34 static int wf_max6690_get(struct wf_sensor *sr, s32 *value) 35 { 36 struct wf_6690_sensor *max = wf_to_6690(sr); 37 s32 data; 38 39 if (max->i2c == NULL) 40 return -ENODEV; 41 42 /* chip gets initialized by firmware */ 43 data = i2c_smbus_read_byte_data(max->i2c, MAX6690_EXTERNAL_TEMP); 44 if (data < 0) 45 return data; 46 *value = data << 16; 47 return 0; 48 } 49 50 static void wf_max6690_release(struct wf_sensor *sr) 51 { 52 struct wf_6690_sensor *max = wf_to_6690(sr); 53 54 kfree(max); 55 } 56 57 static const struct wf_sensor_ops wf_max6690_ops = { 58 .get_value = wf_max6690_get, 59 .release = wf_max6690_release, 60 .owner = THIS_MODULE, 61 }; 62 63 static int wf_max6690_probe(struct i2c_client *client, 64 const struct i2c_device_id *id) 65 { 66 const char *name, *loc; 67 struct wf_6690_sensor *max; 68 int rc; 69 70 loc = of_get_property(client->dev.of_node, "hwsensor-location", NULL); 71 if (!loc) { 72 dev_warn(&client->dev, "Missing hwsensor-location property!\n"); 73 return -ENXIO; 74 } 75 76 /* 77 * We only expose the external temperature register for 78 * now as this is all we need for our control loops 79 */ 80 if (!strcmp(loc, "BACKSIDE") || !strcmp(loc, "SYS CTRLR AMBIENT")) 81 name = "backside-temp"; 82 else if (!strcmp(loc, "NB Ambient")) 83 name = "north-bridge-temp"; 84 else if (!strcmp(loc, "GPU Ambient")) 85 name = "gpu-temp"; 86 else 87 return -ENXIO; 88 89 max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); 90 if (max == NULL) { 91 printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor: " 92 "no memory\n"); 93 return -ENOMEM; 94 } 95 96 max->i2c = client; 97 max->sens.name = name; 98 max->sens.ops = &wf_max6690_ops; 99 i2c_set_clientdata(client, max); 100 101 rc = wf_register_sensor(&max->sens); 102 if (rc) 103 kfree(max); 104 return rc; 105 } 106 107 static int wf_max6690_remove(struct i2c_client *client) 108 { 109 struct wf_6690_sensor *max = i2c_get_clientdata(client); 110 111 max->i2c = NULL; 112 wf_unregister_sensor(&max->sens); 113 114 return 0; 115 } 116 117 static const struct i2c_device_id wf_max6690_id[] = { 118 { "MAC,max6690", 0 }, 119 { } 120 }; 121 MODULE_DEVICE_TABLE(i2c, wf_max6690_id); 122 123 static const struct of_device_id wf_max6690_of_id[] = { 124 { .compatible = "max6690", }, 125 { } 126 }; 127 MODULE_DEVICE_TABLE(of, wf_max6690_of_id); 128 129 static struct i2c_driver wf_max6690_driver = { 130 .driver = { 131 .name = "wf_max6690", 132 .of_match_table = wf_max6690_of_id, 133 }, 134 .probe = wf_max6690_probe, 135 .remove = wf_max6690_remove, 136 .id_table = wf_max6690_id, 137 }; 138 139 module_i2c_driver(wf_max6690_driver); 140 141 MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); 142 MODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control"); 143 MODULE_LICENSE("GPL"); 144