blob: bfef223957728213c167db31c61c26b15f723240 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 smsc47m1.c - Part of lm_sensors, Linux kernel modules
3 for hardware monitoring
4
Jean Delvare60917802006-10-08 22:00:44 +02005 Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x,
Jean Delvare8eccbb62007-05-08 17:21:59 +02006 LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997
7 Super-I/O chips.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008
9 Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
Jean Delvare8eccbb62007-05-08 17:21:59 +020010 Copyright (C) 2004-2007 Jean Delvare <khali@linux-fr.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
12 and Jean Delvare
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27*/
28
29#include <linux/module.h>
30#include <linux/slab.h>
31#include <linux/ioport.h>
32#include <linux/jiffies.h>
Jean Delvare51f2cca2007-05-08 17:22:00 +020033#include <linux/platform_device.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040034#include <linux/hwmon.h>
Jean Delvaree84cfbc2007-05-08 17:22:00 +020035#include <linux/hwmon-sysfs.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040036#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010038#include <linux/mutex.h>
Jean Delvarece8c6ce12006-09-24 21:25:12 +020039#include <linux/sysfs.h>
Jean Delvareb9acb642009-01-07 16:37:35 +010040#include <linux/acpi.h>
H Hartley Sweeten6055fae2009-09-15 17:18:13 +020041#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Jean Delvare67b671b2007-12-06 23:13:42 +010043static unsigned short force_id;
44module_param(force_id, ushort, 0);
45MODULE_PARM_DESC(force_id, "Override the detected device ID");
46
Jean Delvare51f2cca2007-05-08 17:22:00 +020047static struct platform_device *pdev;
48
49#define DRVNAME "smsc47m1"
Jean Delvare8eccbb62007-05-08 17:21:59 +020050enum chips { smsc47m1, smsc47m2 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52/* Super-I/0 registers and commands */
53
54#define REG 0x2e /* The register to read/write */
55#define VAL 0x2f /* The value to read/write */
56
57static inline void
58superio_outb(int reg, int val)
59{
60 outb(reg, REG);
61 outb(val, VAL);
62}
63
64static inline int
65superio_inb(int reg)
66{
67 outb(reg, REG);
68 return inb(VAL);
69}
70
71/* logical device for fans is 0x0A */
72#define superio_select() superio_outb(0x07, 0x0A)
73
74static inline void
75superio_enter(void)
76{
77 outb(0x55, REG);
78}
79
80static inline void
81superio_exit(void)
82{
83 outb(0xAA, REG);
84}
85
86#define SUPERIO_REG_ACT 0x30
87#define SUPERIO_REG_BASE 0x60
88#define SUPERIO_REG_DEVID 0x20
Jean Delvare1b54ab42009-07-28 16:31:39 +020089#define SUPERIO_REG_DEVREV 0x21
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
91/* Logical device registers */
92
93#define SMSC_EXTENT 0x80
94
95/* nr is 0 or 1 in the macros below */
96#define SMSC47M1_REG_ALARM 0x04
97#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
98#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070099#define SMSC47M1_REG_FANDIV 0x58
Jean Delvare8eccbb62007-05-08 17:21:59 +0200100
101static const u8 SMSC47M1_REG_FAN[3] = { 0x59, 0x5a, 0x6b };
102static const u8 SMSC47M1_REG_FAN_PRELOAD[3] = { 0x5b, 0x5c, 0x6c };
103static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 };
104
105#define SMSC47M2_REG_ALARM6 0x09
106#define SMSC47M2_REG_TPIN1 0x38
107#define SMSC47M2_REG_TPIN2 0x37
108#define SMSC47M2_REG_TPIN3 0x2d
109#define SMSC47M2_REG_PPIN3 0x2c
110#define SMSC47M2_REG_FANDIV3 0x6a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
112#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
113 983040/((192-(reg))*(div)))
114#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
115 983040/(((reg)-(preload))*(div)))
116#define DIV_FROM_REG(reg) (1 << (reg))
117#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
118#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
119#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
120
121struct smsc47m1_data {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200122 unsigned short addr;
123 const char *name;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200124 enum chips type;
Tony Jones1beeffe2007-08-20 13:46:20 -0700125 struct device *hwmon_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100127 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 unsigned long last_updated; /* In jiffies */
129
Jean Delvare8eccbb62007-05-08 17:21:59 +0200130 u8 fan[3]; /* Register value */
131 u8 fan_preload[3]; /* Register value */
132 u8 fan_div[3]; /* Register encoding, shifted right */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 u8 alarms; /* Register encoding */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200134 u8 pwm[3]; /* Register value (bit 0 is disable) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135};
136
Jean Delvare51f2cca2007-05-08 17:22:00 +0200137struct smsc47m1_sio_data {
138 enum chips type;
139};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140
Jean Delvare51f2cca2007-05-08 17:22:00 +0200141
142static int smsc47m1_probe(struct platform_device *pdev);
Jean Delvared0546122007-07-22 12:09:48 +0200143static int __devexit smsc47m1_remove(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
145 int init);
146
Jean Delvare51f2cca2007-05-08 17:22:00 +0200147static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg)
Jean Delvare94e183f2007-05-08 17:21:59 +0200148{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200149 return inb_p(data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200150}
151
Jean Delvare51f2cca2007-05-08 17:22:00 +0200152static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg,
Jean Delvare94e183f2007-05-08 17:21:59 +0200153 u8 value)
154{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200155 outb_p(value, data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200156}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
Jean Delvare51f2cca2007-05-08 17:22:00 +0200158static struct platform_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100159 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200160 .owner = THIS_MODULE,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200161 .name = DRVNAME,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100162 },
Jean Delvare51f2cca2007-05-08 17:22:00 +0200163 .probe = smsc47m1_probe,
164 .remove = __devexit_p(smsc47m1_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165};
166
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200167static ssize_t get_fan(struct device *dev, struct device_attribute
168 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200170 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200172 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 /* This chip (stupidly) stops monitoring fan speed if PWM is
174 enabled and duty cycle is 0%. This is fine if the monitoring
175 and control concern the same fan, but troublesome if they are
176 not (which could as well happen). */
177 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
178 FAN_FROM_REG(data->fan[nr],
179 DIV_FROM_REG(data->fan_div[nr]),
180 data->fan_preload[nr]);
181 return sprintf(buf, "%d\n", rpm);
182}
183
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200184static ssize_t get_fan_min(struct device *dev, struct device_attribute
185 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200187 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200189 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 int rpm = MIN_FROM_REG(data->fan_preload[nr],
191 DIV_FROM_REG(data->fan_div[nr]));
192 return sprintf(buf, "%d\n", rpm);
193}
194
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200195static ssize_t get_fan_div(struct device *dev, struct device_attribute
196 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200198 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200200 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202
Jean Delvare1f08af72008-01-06 15:36:13 +0100203static ssize_t get_fan_alarm(struct device *dev, struct device_attribute
204 *devattr, char *buf)
205{
206 int bitnr = to_sensor_dev_attr(devattr)->index;
207 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
208 return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
209}
210
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200211static ssize_t get_pwm(struct device *dev, struct device_attribute
212 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200214 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200216 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217}
218
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200219static ssize_t get_pwm_en(struct device *dev, struct device_attribute
220 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200222 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200224 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225}
226
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200227static ssize_t get_alarms(struct device *dev, struct device_attribute
228 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229{
230 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
231 return sprintf(buf, "%d\n", data->alarms);
232}
233
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200234static ssize_t set_fan_min(struct device *dev, struct device_attribute
235 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200237 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200238 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200239 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 long rpmdiv, val = simple_strtol(buf, NULL, 10);
241
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100242 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
244
245 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100246 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 return -EINVAL;
248 }
249
250 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200251 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100253 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
255 return count;
256}
257
258/* Note: we save and restore the fan minimum here, because its value is
259 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200260 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 because the divider changed. */
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200262static ssize_t set_fan_div(struct device *dev, struct device_attribute
263 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200265 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200266 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200267 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 long new_div = simple_strtol(buf, NULL, 10), tmp;
269 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
270
271 if (new_div == old_div) /* No change */
272 return count;
273
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100274 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 switch (new_div) {
276 case 1: data->fan_div[nr] = 0; break;
277 case 2: data->fan_div[nr] = 1; break;
278 case 4: data->fan_div[nr] = 2; break;
279 case 8: data->fan_div[nr] = 3; break;
280 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100281 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 return -EINVAL;
283 }
284
Jean Delvare8eccbb62007-05-08 17:21:59 +0200285 switch (nr) {
286 case 0:
287 case 1:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200288 tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200289 & ~(0x03 << (4 + 2 * nr));
290 tmp |= data->fan_div[nr] << (4 + 2 * nr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200291 smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200292 break;
293 case 2:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200294 tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200295 tmp |= data->fan_div[2] << 4;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200296 smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200297 break;
298 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299
300 /* Preserve fan min */
301 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
302 + new_div / 2) / new_div;
303 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200304 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100306 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
308 return count;
309}
310
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200311static ssize_t set_pwm(struct device *dev, struct device_attribute
312 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200314 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200315 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200316 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 long val = simple_strtol(buf, NULL, 10);
318
319 if (val < 0 || val > 255)
320 return -EINVAL;
321
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100322 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 data->pwm[nr] &= 0x81; /* Preserve additional bits */
324 data->pwm[nr] |= PWM_TO_REG(val);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200325 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100327 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
329 return count;
330}
331
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200332static ssize_t set_pwm_en(struct device *dev, struct device_attribute
333 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200335 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200336 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200337 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 long val = simple_strtol(buf, NULL, 10);
339
340 if (val != 0 && val != 1)
341 return -EINVAL;
342
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100343 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 data->pwm[nr] &= 0xFE; /* preserve the other bits */
345 data->pwm[nr] |= !val;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200346 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100348 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
350 return count;
351}
352
353#define fan_present(offset) \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200354static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan, \
355 NULL, offset - 1); \
356static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
357 get_fan_min, set_fan_min, offset - 1); \
358static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
359 get_fan_div, set_fan_div, offset - 1); \
Jean Delvare1f08af72008-01-06 15:36:13 +0100360static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm, \
361 NULL, offset - 1); \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200362static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
363 get_pwm, set_pwm, offset - 1); \
364static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
365 get_pwm_en, set_pwm_en, offset - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367fan_present(1);
368fan_present(2);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200369fan_present(3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
371static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
372
Jean Delvare51f2cca2007-05-08 17:22:00 +0200373static ssize_t show_name(struct device *dev, struct device_attribute
374 *devattr, char *buf)
375{
376 struct smsc47m1_data *data = dev_get_drvdata(dev);
377
378 return sprintf(buf, "%s\n", data->name);
379}
380static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
381
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200382/* Almost all sysfs files may or may not be created depending on the chip
383 setup so we create them individually. It is still convenient to define a
384 group to remove them all at once. */
385static struct attribute *smsc47m1_attributes[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200386 &sensor_dev_attr_fan1_input.dev_attr.attr,
387 &sensor_dev_attr_fan1_min.dev_attr.attr,
388 &sensor_dev_attr_fan1_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100389 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200390 &sensor_dev_attr_fan2_input.dev_attr.attr,
391 &sensor_dev_attr_fan2_min.dev_attr.attr,
392 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100393 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200394 &sensor_dev_attr_fan3_input.dev_attr.attr,
395 &sensor_dev_attr_fan3_min.dev_attr.attr,
396 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100397 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200398
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200399 &sensor_dev_attr_pwm1.dev_attr.attr,
400 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
401 &sensor_dev_attr_pwm2.dev_attr.attr,
402 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
403 &sensor_dev_attr_pwm3.dev_attr.attr,
404 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200405
406 &dev_attr_alarms.attr,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200407 &dev_attr_name.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200408 NULL
409};
410
411static const struct attribute_group smsc47m1_group = {
412 .attrs = smsc47m1_attributes,
413};
414
Jean Delvare51f2cca2007-05-08 17:22:00 +0200415static int __init smsc47m1_find(unsigned short *addr,
416 struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417{
418 u8 val;
419
420 superio_enter();
Jean Delvare67b671b2007-12-06 23:13:42 +0100421 val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422
423 /*
Jean Delvare60917802006-10-08 22:00:44 +0200424 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
425 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200427 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200428 * The LPC47M997 is undocumented, but seems to be compatible with
429 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200430 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
431 * supports a 3rd fan, and the pin configuration registers are
432 * unfortunately different.
Jean Delvare1b54ab42009-07-28 16:31:39 +0200433 * The LPC47M233 has the same device id (0x6B) but is not compatible.
434 * We check the high bit of the device revision register to
435 * differentiate them.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200437 switch (val) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200438 case 0x51:
Jean Delvare620100c2007-05-08 17:22:00 +0200439 pr_info(DRVNAME ": Found SMSC LPC47B27x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200440 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200441 break;
442 case 0x59:
Jean Delvare620100c2007-05-08 17:22:00 +0200443 pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200444 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200445 break;
446 case 0x5F:
Jean Delvare620100c2007-05-08 17:22:00 +0200447 pr_info(DRVNAME ": Found SMSC LPC47M14x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200448 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200449 break;
450 case 0x60:
Jean Delvare620100c2007-05-08 17:22:00 +0200451 pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200452 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200453 break;
454 case 0x6B:
Jean Delvare1b54ab42009-07-28 16:31:39 +0200455 if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
456 pr_debug(DRVNAME ": "
457 "Found SMSC LPC47M233, unsupported\n");
458 superio_exit();
459 return -ENODEV;
460 }
461
Jean Delvare620100c2007-05-08 17:22:00 +0200462 pr_info(DRVNAME ": Found SMSC LPC47M292\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200463 sio_data->type = smsc47m2;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200464 break;
465 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 superio_exit();
467 return -ENODEV;
468 }
469
470 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200471 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
472 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200474 if (*addr == 0 || (val & 0x01) == 0) {
Jean Delvare620100c2007-05-08 17:22:00 +0200475 pr_info(DRVNAME ": Device is disabled, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 superio_exit();
477 return -ENODEV;
478 }
479
480 superio_exit();
481 return 0;
482}
483
Jean Delvarea0e92d72009-12-16 21:38:26 +0100484#define CHECK 1
485#define REQUEST 2
486#define RELEASE 3
487
488/*
489 * This function can be used to:
490 * - test for resource conflicts with ACPI
491 * - request the resources
492 * - release the resources
493 * We only allocate the I/O ports we really need, to minimize the risk of
494 * conflicts with ACPI or with other drivers.
495 */
496static int smsc47m1_handle_resources(unsigned short address, enum chips type,
497 int action, struct device *dev)
498{
499 static const u8 ports_m1[] = {
500 /* register, region length */
501 0x04, 1,
502 0x33, 4,
503 0x56, 7,
504 };
505
506 static const u8 ports_m2[] = {
507 /* register, region length */
508 0x04, 1,
509 0x09, 1,
510 0x2c, 2,
511 0x35, 4,
512 0x56, 7,
513 0x69, 4,
514 };
515
516 int i, ports_size, err;
517 const u8 *ports;
518
519 switch (type) {
520 case smsc47m1:
521 default:
522 ports = ports_m1;
523 ports_size = ARRAY_SIZE(ports_m1);
524 break;
525 case smsc47m2:
526 ports = ports_m2;
527 ports_size = ARRAY_SIZE(ports_m2);
528 break;
529 }
530
531 for (i = 0; i + 1 < ports_size; i += 2) {
532 unsigned short start = address + ports[i];
533 unsigned short len = ports[i + 1];
534
535 switch (action) {
536 case CHECK:
537 /* Only check for conflicts */
538 err = acpi_check_region(start, len, DRVNAME);
539 if (err)
540 return err;
541 break;
542 case REQUEST:
543 /* Request the resources */
544 if (!request_region(start, len, DRVNAME)) {
545 dev_err(dev, "Region 0x%hx-0x%hx already in "
546 "use!\n", start, start + len);
547
548 /* Undo all requests */
549 for (i -= 2; i >= 0; i -= 2)
550 release_region(address + ports[i],
551 ports[i + 1]);
552 return -EBUSY;
553 }
554 break;
555 case RELEASE:
556 /* Release the resources */
557 release_region(start, len);
558 break;
559 }
560 }
561
562 return 0;
563}
564
Jean Delvare51f2cca2007-05-08 17:22:00 +0200565static int __devinit smsc47m1_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200567 struct device *dev = &pdev->dev;
568 struct smsc47m1_sio_data *sio_data = dev->platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 struct smsc47m1_data *data;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200570 struct resource *res;
Jean Delvarea0e92d72009-12-16 21:38:26 +0100571 int err;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200572 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
Jean Delvare51f2cca2007-05-08 17:22:00 +0200574 static const char *names[] = {
575 "smsc47m1",
576 "smsc47m2",
577 };
578
579 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100580 err = smsc47m1_handle_resources(res->start, sio_data->type,
581 REQUEST, dev);
582 if (err < 0)
583 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200585 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 err = -ENOMEM;
587 goto error_release;
588 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
Jean Delvare51f2cca2007-05-08 17:22:00 +0200590 data->addr = res->start;
591 data->type = sio_data->type;
592 data->name = names[sio_data->type];
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100593 mutex_init(&data->update_lock);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200594 platform_set_drvdata(pdev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
596 /* If no function is properly configured, there's no point in
597 actually registering the chip. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200598 pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 == 0x04;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200600 pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200602 if (data->type == smsc47m2) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200603 fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200604 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200605 fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200606 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200607 fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200608 & 0x0d) == 0x0d;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200609 pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200610 & 0x0d) == 0x08;
611 } else {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200612 fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200613 & 0x05) == 0x05;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200614 fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200615 & 0x05) == 0x05;
616 fan3 = 0;
617 pwm3 = 0;
618 }
619 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200620 dev_warn(dev, "Device not configured, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 err = -ENODEV;
622 goto error_free;
623 }
624
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 /* Some values (fan min, clock dividers, pwm registers) may be
626 needed before any update is triggered, so we better read them
627 at least once here. We don't usually do it that way, but in
628 this particular case, manually reading 5 registers out of 8
629 doesn't make much sense and we're better using the existing
630 function. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200631 smsc47m1_update_device(dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400633 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634 if (fan1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200635 if ((err = device_create_file(dev,
636 &sensor_dev_attr_fan1_input.dev_attr))
637 || (err = device_create_file(dev,
638 &sensor_dev_attr_fan1_min.dev_attr))
639 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100640 &sensor_dev_attr_fan1_div.dev_attr))
641 || (err = device_create_file(dev,
642 &sensor_dev_attr_fan1_alarm.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200643 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200645 dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646
647 if (fan2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200648 if ((err = device_create_file(dev,
649 &sensor_dev_attr_fan2_input.dev_attr))
650 || (err = device_create_file(dev,
651 &sensor_dev_attr_fan2_min.dev_attr))
652 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100653 &sensor_dev_attr_fan2_div.dev_attr))
654 || (err = device_create_file(dev,
655 &sensor_dev_attr_fan2_alarm.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200656 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200658 dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
Jean Delvare8eccbb62007-05-08 17:21:59 +0200660 if (fan3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200661 if ((err = device_create_file(dev,
662 &sensor_dev_attr_fan3_input.dev_attr))
663 || (err = device_create_file(dev,
664 &sensor_dev_attr_fan3_min.dev_attr))
665 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100666 &sensor_dev_attr_fan3_div.dev_attr))
667 || (err = device_create_file(dev,
668 &sensor_dev_attr_fan3_alarm.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200669 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200670 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200671 dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200672
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 if (pwm1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200674 if ((err = device_create_file(dev,
675 &sensor_dev_attr_pwm1.dev_attr))
676 || (err = device_create_file(dev,
677 &sensor_dev_attr_pwm1_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200678 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200680 dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200681
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 if (pwm2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200683 if ((err = device_create_file(dev,
684 &sensor_dev_attr_pwm2.dev_attr))
685 || (err = device_create_file(dev,
686 &sensor_dev_attr_pwm2_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200687 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200689 dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Jean Delvare8eccbb62007-05-08 17:21:59 +0200691 if (pwm3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200692 if ((err = device_create_file(dev,
693 &sensor_dev_attr_pwm3.dev_attr))
694 || (err = device_create_file(dev,
695 &sensor_dev_attr_pwm3_enable.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200696 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200697 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200698 dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200699
Jean Delvare51f2cca2007-05-08 17:22:00 +0200700 if ((err = device_create_file(dev, &dev_attr_alarms)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200701 goto error_remove_files;
Jean Delvare68a50b52007-08-12 13:58:50 +0200702 if ((err = device_create_file(dev, &dev_attr_name)))
703 goto error_remove_files;
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200704
Tony Jones1beeffe2007-08-20 13:46:20 -0700705 data->hwmon_dev = hwmon_device_register(dev);
706 if (IS_ERR(data->hwmon_dev)) {
707 err = PTR_ERR(data->hwmon_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200708 goto error_remove_files;
709 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 return 0;
712
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200713error_remove_files:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200714 sysfs_remove_group(&dev->kobj, &smsc47m1_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715error_free:
Jean Delvare04a62172007-06-12 13:57:19 +0200716 platform_set_drvdata(pdev, NULL);
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400717 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718error_release:
Jean Delvarea0e92d72009-12-16 21:38:26 +0100719 smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 return err;
721}
722
Jean Delvare51f2cca2007-05-08 17:22:00 +0200723static int __devexit smsc47m1_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200725 struct smsc47m1_data *data = platform_get_drvdata(pdev);
726 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727
Tony Jones1beeffe2007-08-20 13:46:20 -0700728 hwmon_device_unregister(data->hwmon_dev);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200729 sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400730
Jean Delvare51f2cca2007-05-08 17:22:00 +0200731 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100732 smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev);
Jean Delvare04a62172007-06-12 13:57:19 +0200733 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400734 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
736 return 0;
737}
738
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
740 int init)
741{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200742 struct smsc47m1_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100744 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
746 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200747 int i, fan_nr;
748 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Jean Delvare8eccbb62007-05-08 17:21:59 +0200750 for (i = 0; i < fan_nr; i++) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200751 data->fan[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200752 SMSC47M1_REG_FAN[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200753 data->fan_preload[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200754 SMSC47M1_REG_FAN_PRELOAD[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200755 data->pwm[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200756 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 }
758
Jean Delvare51f2cca2007-05-08 17:22:00 +0200759 i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 data->fan_div[0] = (i >> 4) & 0x03;
761 data->fan_div[1] = i >> 6;
762
Jean Delvare51f2cca2007-05-08 17:22:00 +0200763 data->alarms = smsc47m1_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 SMSC47M1_REG_ALARM) >> 6;
765 /* Clear alarms if needed */
766 if (data->alarms)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200767 smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768
Jean Delvare8eccbb62007-05-08 17:21:59 +0200769 if (fan_nr >= 3) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200770 data->fan_div[2] = (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200771 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200772 data->alarms |= (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200773 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
774 /* Clear alarm if needed */
775 if (data->alarms & 0x04)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200776 smsc47m1_write_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200777 SMSC47M2_REG_ALARM6,
778 0x40);
779 }
780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 data->last_updated = jiffies;
782 }
783
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100784 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 return data;
786}
787
Jean Delvare51f2cca2007-05-08 17:22:00 +0200788static int __init smsc47m1_device_add(unsigned short address,
789 const struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200791 struct resource res = {
792 .start = address,
793 .end = address + SMSC_EXTENT - 1,
794 .name = DRVNAME,
795 .flags = IORESOURCE_IO,
796 };
797 int err;
798
Jean Delvarea0e92d72009-12-16 21:38:26 +0100799 err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
Jean Delvareb9acb642009-01-07 16:37:35 +0100800 if (err)
801 goto exit;
802
Jean Delvare51f2cca2007-05-08 17:22:00 +0200803 pdev = platform_device_alloc(DRVNAME, address);
804 if (!pdev) {
805 err = -ENOMEM;
806 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
807 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 }
809
Jean Delvare51f2cca2007-05-08 17:22:00 +0200810 err = platform_device_add_resources(pdev, &res, 1);
811 if (err) {
812 printk(KERN_ERR DRVNAME ": Device resource addition failed "
813 "(%d)\n", err);
814 goto exit_device_put;
815 }
816
Jean Delvare2df6d812007-06-09 10:11:16 -0400817 err = platform_device_add_data(pdev, sio_data,
818 sizeof(struct smsc47m1_sio_data));
819 if (err) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200820 printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
821 goto exit_device_put;
822 }
Jean Delvare51f2cca2007-05-08 17:22:00 +0200823
824 err = platform_device_add(pdev);
825 if (err) {
826 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
827 err);
828 goto exit_device_put;
829 }
830
831 return 0;
832
833exit_device_put:
834 platform_device_put(pdev);
835exit:
836 return err;
837}
838
839static int __init sm_smsc47m1_init(void)
840{
841 int err;
842 unsigned short address;
843 struct smsc47m1_sio_data sio_data;
844
845 if (smsc47m1_find(&address, &sio_data))
846 return -ENODEV;
847
848 err = platform_driver_register(&smsc47m1_driver);
849 if (err)
850 goto exit;
851
852 /* Sets global pdev as a side effect */
853 err = smsc47m1_device_add(address, &sio_data);
854 if (err)
855 goto exit_driver;
856
857 return 0;
858
859exit_driver:
860 platform_driver_unregister(&smsc47m1_driver);
861exit:
862 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863}
864
865static void __exit sm_smsc47m1_exit(void)
866{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200867 platform_device_unregister(pdev);
868 platform_driver_unregister(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869}
870
871MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
872MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
873MODULE_LICENSE("GPL");
874
875module_init(sm_smsc47m1_init);
876module_exit(sm_smsc47m1_exit);