Merge branch 'next' of git://git.monstr.eu/linux-2.6-microblaze
[linux-2.6-microblaze.git] / drivers / hwmon / tc74.c
1 /*
2  * An hwmon driver for the Microchip TC74
3  *
4  * Copyright 2015 Maciej Szmigiero <mail@maciej.szmigiero.name>
5  *
6  * Based on ad7414.c:
7  *      Copyright 2006 Stefan Roese, DENX Software Engineering
8  *      Copyright 2008 Sean MacLennan, PIKA Technologies
9  *      Copyright 2008 Frank Edelhaeuser, Spansion Inc.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  */
16
17 #include <linux/bitops.h>
18 #include <linux/err.h>
19 #include <linux/hwmon.h>
20 #include <linux/hwmon-sysfs.h>
21 #include <linux/i2c.h>
22 #include <linux/jiffies.h>
23 #include <linux/module.h>
24 #include <linux/mutex.h>
25 #include <linux/slab.h>
26 #include <linux/sysfs.h>
27
28 /* TC74 registers */
29 #define TC74_REG_TEMP           0x00
30 #define TC74_REG_CONFIG         0x01
31
32 struct tc74_data {
33         struct i2c_client       *client;
34         struct mutex            lock;   /* atomic read data updates */
35         bool                    valid;  /* validity of fields below */
36         unsigned long           next_update;    /* In jiffies */
37         s8                      temp_input;     /* Temp value in dC */
38 };
39
40 static int tc74_update_device(struct device *dev)
41 {
42         struct tc74_data *data = dev_get_drvdata(dev);
43         struct i2c_client *client = data->client;
44         int ret;
45
46         ret = mutex_lock_interruptible(&data->lock);
47         if (ret)
48                 return ret;
49
50         if (time_after(jiffies, data->next_update) || !data->valid) {
51                 s32 value;
52
53                 value = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
54                 if (value < 0) {
55                         dev_dbg(&client->dev, "TC74_REG_CONFIG read err %d\n",
56                                 (int)value);
57
58                         ret = value;
59                         goto ret_unlock;
60                 }
61
62                 if (!(value & BIT(6))) {
63                         /* not ready yet */
64
65                         ret = -EAGAIN;
66                         goto ret_unlock;
67                 }
68
69                 value = i2c_smbus_read_byte_data(client, TC74_REG_TEMP);
70                 if (value < 0) {
71                         dev_dbg(&client->dev, "TC74_REG_TEMP read err %d\n",
72                                 (int)value);
73
74                         ret = value;
75                         goto ret_unlock;
76                 }
77
78                 data->temp_input = value;
79                 data->next_update = jiffies + HZ / 4;
80                 data->valid = true;
81         }
82
83 ret_unlock:
84         mutex_unlock(&data->lock);
85
86         return ret;
87 }
88
89 static ssize_t show_temp_input(struct device *dev,
90                                struct device_attribute *attr, char *buf)
91 {
92         struct tc74_data *data = dev_get_drvdata(dev);
93         int ret;
94
95         ret = tc74_update_device(dev);
96         if (ret)
97                 return ret;
98
99         return sprintf(buf, "%d\n", data->temp_input * 1000);
100 }
101 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL, 0);
102
103 static struct attribute *tc74_attrs[] = {
104         &sensor_dev_attr_temp1_input.dev_attr.attr,
105         NULL
106 };
107
108 ATTRIBUTE_GROUPS(tc74);
109
110 static int tc74_probe(struct i2c_client *client,
111                       const struct i2c_device_id *dev_id)
112 {
113         struct device *dev = &client->dev;
114         struct tc74_data *data;
115         struct device *hwmon_dev;
116         s32 conf;
117
118         if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
119                 return -EOPNOTSUPP;
120
121         data = devm_kzalloc(dev, sizeof(struct tc74_data), GFP_KERNEL);
122         if (!data)
123                 return -ENOMEM;
124
125         data->client = client;
126         mutex_init(&data->lock);
127
128         /* Make sure the chip is powered up. */
129         conf = i2c_smbus_read_byte_data(client, TC74_REG_CONFIG);
130         if (conf < 0) {
131                 dev_err(dev, "unable to read config register\n");
132
133                 return conf;
134         }
135
136         if (conf & 0x3f) {
137                 dev_err(dev, "invalid config register value\n");
138
139                 return -ENODEV;
140         }
141
142         if (conf & BIT(7)) {
143                 s32 ret;
144
145                 conf &= ~BIT(7);
146
147                 ret = i2c_smbus_write_byte_data(client, TC74_REG_CONFIG, conf);
148                 if (ret)
149                         dev_warn(dev, "unable to disable STANDBY\n");
150         }
151
152         hwmon_dev = devm_hwmon_device_register_with_groups(dev,
153                                                            client->name,
154                                                            data, tc74_groups);
155         return PTR_ERR_OR_ZERO(hwmon_dev);
156 }
157
158 static const struct i2c_device_id tc74_id[] = {
159         { "tc74", 0 },
160         {}
161 };
162 MODULE_DEVICE_TABLE(i2c, tc74_id);
163
164 static struct i2c_driver tc74_driver = {
165         .driver = {
166                 .name   = "tc74",
167         },
168         .probe  = tc74_probe,
169         .id_table = tc74_id,
170 };
171
172 module_i2c_driver(tc74_driver);
173
174 MODULE_AUTHOR("Maciej Szmigiero <mail@maciej.szmigiero.name>");
175
176 MODULE_DESCRIPTION("TC74 driver");
177 MODULE_LICENSE("GPL");