blob: 8fa462f2b570e7c8692d62bbcc5ff5cb8675d781 [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;
Jean Delvarefa0bff02009-12-16 21:38:27 +0100139 u8 activate; /* Remember initial device state */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200140};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
Jean Delvare51f2cca2007-05-08 17:22:00 +0200142
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100143static int __exit 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 Delvare3ecf44b2009-12-16 21:38:26 +0100163 .remove = __exit_p(smsc47m1_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164};
165
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200166static ssize_t get_fan(struct device *dev, struct device_attribute
167 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200169 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200171 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 /* This chip (stupidly) stops monitoring fan speed if PWM is
173 enabled and duty cycle is 0%. This is fine if the monitoring
174 and control concern the same fan, but troublesome if they are
175 not (which could as well happen). */
176 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
177 FAN_FROM_REG(data->fan[nr],
178 DIV_FROM_REG(data->fan_div[nr]),
179 data->fan_preload[nr]);
180 return sprintf(buf, "%d\n", rpm);
181}
182
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200183static ssize_t get_fan_min(struct device *dev, struct device_attribute
184 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200186 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200188 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 int rpm = MIN_FROM_REG(data->fan_preload[nr],
190 DIV_FROM_REG(data->fan_div[nr]));
191 return sprintf(buf, "%d\n", rpm);
192}
193
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200194static ssize_t get_fan_div(struct device *dev, struct device_attribute
195 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200197 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200199 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200}
201
Jean Delvare1f08af72008-01-06 15:36:13 +0100202static ssize_t get_fan_alarm(struct device *dev, struct device_attribute
203 *devattr, char *buf)
204{
205 int bitnr = to_sensor_dev_attr(devattr)->index;
206 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
207 return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
208}
209
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200210static ssize_t get_pwm(struct device *dev, struct device_attribute
211 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200213 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200215 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216}
217
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200218static ssize_t get_pwm_en(struct device *dev, struct device_attribute
219 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200221 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200223 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224}
225
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200226static ssize_t get_alarms(struct device *dev, struct device_attribute
227 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
229 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
230 return sprintf(buf, "%d\n", data->alarms);
231}
232
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200233static ssize_t set_fan_min(struct device *dev, struct device_attribute
234 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200236 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200237 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200238 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 long rpmdiv, val = simple_strtol(buf, NULL, 10);
240
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100241 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
243
244 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100245 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 return -EINVAL;
247 }
248
249 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200250 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100252 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
254 return count;
255}
256
257/* Note: we save and restore the fan minimum here, because its value is
258 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200259 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 because the divider changed. */
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200261static ssize_t set_fan_div(struct device *dev, struct device_attribute
262 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200264 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200265 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200266 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 long new_div = simple_strtol(buf, NULL, 10), tmp;
268 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
269
270 if (new_div == old_div) /* No change */
271 return count;
272
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100273 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 switch (new_div) {
275 case 1: data->fan_div[nr] = 0; break;
276 case 2: data->fan_div[nr] = 1; break;
277 case 4: data->fan_div[nr] = 2; break;
278 case 8: data->fan_div[nr] = 3; break;
279 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100280 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 return -EINVAL;
282 }
283
Jean Delvare8eccbb62007-05-08 17:21:59 +0200284 switch (nr) {
285 case 0:
286 case 1:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200287 tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200288 & ~(0x03 << (4 + 2 * nr));
289 tmp |= data->fan_div[nr] << (4 + 2 * nr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200290 smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200291 break;
292 case 2:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200293 tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200294 tmp |= data->fan_div[2] << 4;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200295 smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200296 break;
297 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 /* Preserve fan min */
300 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
301 + new_div / 2) / new_div;
302 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200303 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100305 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
307 return count;
308}
309
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200310static ssize_t set_pwm(struct device *dev, struct device_attribute
311 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200313 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200314 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200315 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 long val = simple_strtol(buf, NULL, 10);
317
318 if (val < 0 || val > 255)
319 return -EINVAL;
320
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100321 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 data->pwm[nr] &= 0x81; /* Preserve additional bits */
323 data->pwm[nr] |= PWM_TO_REG(val);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200324 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100326 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328 return count;
329}
330
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200331static ssize_t set_pwm_en(struct device *dev, struct device_attribute
332 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200334 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200335 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200336 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 long val = simple_strtol(buf, NULL, 10);
338
339 if (val != 0 && val != 1)
340 return -EINVAL;
341
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100342 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 data->pwm[nr] &= 0xFE; /* preserve the other bits */
344 data->pwm[nr] |= !val;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200345 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100347 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
349 return count;
350}
351
352#define fan_present(offset) \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200353static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan, \
354 NULL, offset - 1); \
355static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
356 get_fan_min, set_fan_min, offset - 1); \
357static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
358 get_fan_div, set_fan_div, offset - 1); \
Jean Delvare1f08af72008-01-06 15:36:13 +0100359static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm, \
360 NULL, offset - 1); \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200361static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
362 get_pwm, set_pwm, offset - 1); \
363static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
364 get_pwm_en, set_pwm_en, offset - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
366fan_present(1);
367fan_present(2);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200368fan_present(3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369
370static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
371
Jean Delvare51f2cca2007-05-08 17:22:00 +0200372static ssize_t show_name(struct device *dev, struct device_attribute
373 *devattr, char *buf)
374{
375 struct smsc47m1_data *data = dev_get_drvdata(dev);
376
377 return sprintf(buf, "%s\n", data->name);
378}
379static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
380
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200381/* Almost all sysfs files may or may not be created depending on the chip
382 setup so we create them individually. It is still convenient to define a
383 group to remove them all at once. */
384static struct attribute *smsc47m1_attributes[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200385 &sensor_dev_attr_fan1_input.dev_attr.attr,
386 &sensor_dev_attr_fan1_min.dev_attr.attr,
387 &sensor_dev_attr_fan1_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100388 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200389 &sensor_dev_attr_fan2_input.dev_attr.attr,
390 &sensor_dev_attr_fan2_min.dev_attr.attr,
391 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100392 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200393 &sensor_dev_attr_fan3_input.dev_attr.attr,
394 &sensor_dev_attr_fan3_min.dev_attr.attr,
395 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100396 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200397
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200398 &sensor_dev_attr_pwm1.dev_attr.attr,
399 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
400 &sensor_dev_attr_pwm2.dev_attr.attr,
401 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
402 &sensor_dev_attr_pwm3.dev_attr.attr,
403 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200404
405 &dev_attr_alarms.attr,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200406 &dev_attr_name.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200407 NULL
408};
409
410static const struct attribute_group smsc47m1_group = {
411 .attrs = smsc47m1_attributes,
412};
413
Jean Delvare51f2cca2007-05-08 17:22:00 +0200414static int __init smsc47m1_find(unsigned short *addr,
415 struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416{
417 u8 val;
418
419 superio_enter();
Jean Delvare67b671b2007-12-06 23:13:42 +0100420 val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
422 /*
Jean Delvare60917802006-10-08 22:00:44 +0200423 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
424 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200426 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200427 * The LPC47M997 is undocumented, but seems to be compatible with
428 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200429 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
430 * supports a 3rd fan, and the pin configuration registers are
431 * unfortunately different.
Jean Delvare1b54ab42009-07-28 16:31:39 +0200432 * The LPC47M233 has the same device id (0x6B) but is not compatible.
433 * We check the high bit of the device revision register to
434 * differentiate them.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200436 switch (val) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200437 case 0x51:
Jean Delvare620100c2007-05-08 17:22:00 +0200438 pr_info(DRVNAME ": Found SMSC LPC47B27x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200439 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200440 break;
441 case 0x59:
Jean Delvare620100c2007-05-08 17:22:00 +0200442 pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200443 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200444 break;
445 case 0x5F:
Jean Delvare620100c2007-05-08 17:22:00 +0200446 pr_info(DRVNAME ": Found SMSC LPC47M14x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200447 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200448 break;
449 case 0x60:
Jean Delvare620100c2007-05-08 17:22:00 +0200450 pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200451 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200452 break;
453 case 0x6B:
Jean Delvare1b54ab42009-07-28 16:31:39 +0200454 if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
455 pr_debug(DRVNAME ": "
456 "Found SMSC LPC47M233, unsupported\n");
457 superio_exit();
458 return -ENODEV;
459 }
460
Jean Delvare620100c2007-05-08 17:22:00 +0200461 pr_info(DRVNAME ": Found SMSC LPC47M292\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200462 sio_data->type = smsc47m2;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200463 break;
464 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 superio_exit();
466 return -ENODEV;
467 }
468
469 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200470 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
471 | superio_inb(SUPERIO_REG_BASE + 1);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100472 if (*addr == 0) {
473 pr_info(DRVNAME ": Device address not set, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 superio_exit();
475 return -ENODEV;
476 }
477
Jean Delvarefa0bff02009-12-16 21:38:27 +0100478 /* Enable only if address is set (needed at least on the
479 * Compaq Presario S4000NX) */
480 sio_data->activate = superio_inb(SUPERIO_REG_ACT);
481 if ((sio_data->activate & 0x01) == 0) {
482 pr_info(DRVNAME ": Enabling device\n");
483 superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
484 }
485
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 superio_exit();
487 return 0;
488}
489
Jean Delvarefa0bff02009-12-16 21:38:27 +0100490/* Restore device to its initial state */
Jeff Mahoneya00d6432010-01-25 15:00:48 +0100491static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
Jean Delvarefa0bff02009-12-16 21:38:27 +0100492{
493 if ((sio_data->activate & 0x01) == 0) {
494 superio_enter();
495 superio_select();
496
497 pr_info(DRVNAME ": Disabling device\n");
498 superio_outb(SUPERIO_REG_ACT, sio_data->activate);
499
500 superio_exit();
501 }
502}
503
Jean Delvarea0e92d72009-12-16 21:38:26 +0100504#define CHECK 1
505#define REQUEST 2
506#define RELEASE 3
507
508/*
509 * This function can be used to:
510 * - test for resource conflicts with ACPI
511 * - request the resources
512 * - release the resources
513 * We only allocate the I/O ports we really need, to minimize the risk of
514 * conflicts with ACPI or with other drivers.
515 */
516static int smsc47m1_handle_resources(unsigned short address, enum chips type,
517 int action, struct device *dev)
518{
519 static const u8 ports_m1[] = {
520 /* register, region length */
521 0x04, 1,
522 0x33, 4,
523 0x56, 7,
524 };
525
526 static const u8 ports_m2[] = {
527 /* register, region length */
528 0x04, 1,
529 0x09, 1,
530 0x2c, 2,
531 0x35, 4,
532 0x56, 7,
533 0x69, 4,
534 };
535
536 int i, ports_size, err;
537 const u8 *ports;
538
539 switch (type) {
540 case smsc47m1:
541 default:
542 ports = ports_m1;
543 ports_size = ARRAY_SIZE(ports_m1);
544 break;
545 case smsc47m2:
546 ports = ports_m2;
547 ports_size = ARRAY_SIZE(ports_m2);
548 break;
549 }
550
551 for (i = 0; i + 1 < ports_size; i += 2) {
552 unsigned short start = address + ports[i];
553 unsigned short len = ports[i + 1];
554
555 switch (action) {
556 case CHECK:
557 /* Only check for conflicts */
558 err = acpi_check_region(start, len, DRVNAME);
559 if (err)
560 return err;
561 break;
562 case REQUEST:
563 /* Request the resources */
564 if (!request_region(start, len, DRVNAME)) {
565 dev_err(dev, "Region 0x%hx-0x%hx already in "
566 "use!\n", start, start + len);
567
568 /* Undo all requests */
569 for (i -= 2; i >= 0; i -= 2)
570 release_region(address + ports[i],
571 ports[i + 1]);
572 return -EBUSY;
573 }
574 break;
575 case RELEASE:
576 /* Release the resources */
577 release_region(start, len);
578 break;
579 }
580 }
581
582 return 0;
583}
584
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100585static int __init smsc47m1_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200587 struct device *dev = &pdev->dev;
588 struct smsc47m1_sio_data *sio_data = dev->platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 struct smsc47m1_data *data;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200590 struct resource *res;
Jean Delvarea0e92d72009-12-16 21:38:26 +0100591 int err;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200592 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Jean Delvare51f2cca2007-05-08 17:22:00 +0200594 static const char *names[] = {
595 "smsc47m1",
596 "smsc47m2",
597 };
598
599 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100600 err = smsc47m1_handle_resources(res->start, sio_data->type,
601 REQUEST, dev);
602 if (err < 0)
603 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200605 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 err = -ENOMEM;
607 goto error_release;
608 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
Jean Delvare51f2cca2007-05-08 17:22:00 +0200610 data->addr = res->start;
611 data->type = sio_data->type;
612 data->name = names[sio_data->type];
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100613 mutex_init(&data->update_lock);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200614 platform_set_drvdata(pdev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616 /* If no function is properly configured, there's no point in
617 actually registering the chip. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200618 pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 == 0x04;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200620 pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200622 if (data->type == smsc47m2) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200623 fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200624 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200625 fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200626 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200627 fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200628 & 0x0d) == 0x0d;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200629 pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200630 & 0x0d) == 0x08;
631 } else {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200632 fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200633 & 0x05) == 0x05;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200634 fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200635 & 0x05) == 0x05;
636 fan3 = 0;
637 pwm3 = 0;
638 }
639 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200640 dev_warn(dev, "Device not configured, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 err = -ENODEV;
642 goto error_free;
643 }
644
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 /* Some values (fan min, clock dividers, pwm registers) may be
646 needed before any update is triggered, so we better read them
647 at least once here. We don't usually do it that way, but in
648 this particular case, manually reading 5 registers out of 8
649 doesn't make much sense and we're better using the existing
650 function. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200651 smsc47m1_update_device(dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400653 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 if (fan1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200655 if ((err = device_create_file(dev,
656 &sensor_dev_attr_fan1_input.dev_attr))
657 || (err = device_create_file(dev,
658 &sensor_dev_attr_fan1_min.dev_attr))
659 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100660 &sensor_dev_attr_fan1_div.dev_attr))
661 || (err = device_create_file(dev,
662 &sensor_dev_attr_fan1_alarm.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200663 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200665 dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
667 if (fan2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200668 if ((err = device_create_file(dev,
669 &sensor_dev_attr_fan2_input.dev_attr))
670 || (err = device_create_file(dev,
671 &sensor_dev_attr_fan2_min.dev_attr))
672 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100673 &sensor_dev_attr_fan2_div.dev_attr))
674 || (err = device_create_file(dev,
675 &sensor_dev_attr_fan2_alarm.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200676 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200678 dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
Jean Delvare8eccbb62007-05-08 17:21:59 +0200680 if (fan3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200681 if ((err = device_create_file(dev,
682 &sensor_dev_attr_fan3_input.dev_attr))
683 || (err = device_create_file(dev,
684 &sensor_dev_attr_fan3_min.dev_attr))
685 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100686 &sensor_dev_attr_fan3_div.dev_attr))
687 || (err = device_create_file(dev,
688 &sensor_dev_attr_fan3_alarm.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200689 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200690 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200691 dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200692
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 if (pwm1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200694 if ((err = device_create_file(dev,
695 &sensor_dev_attr_pwm1.dev_attr))
696 || (err = device_create_file(dev,
697 &sensor_dev_attr_pwm1_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200698 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200700 dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200701
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 if (pwm2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200703 if ((err = device_create_file(dev,
704 &sensor_dev_attr_pwm2.dev_attr))
705 || (err = device_create_file(dev,
706 &sensor_dev_attr_pwm2_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200707 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200709 dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
Jean Delvare8eccbb62007-05-08 17:21:59 +0200711 if (pwm3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200712 if ((err = device_create_file(dev,
713 &sensor_dev_attr_pwm3.dev_attr))
714 || (err = device_create_file(dev,
715 &sensor_dev_attr_pwm3_enable.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200716 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200717 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200718 dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200719
Jean Delvare51f2cca2007-05-08 17:22:00 +0200720 if ((err = device_create_file(dev, &dev_attr_alarms)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200721 goto error_remove_files;
Jean Delvare68a50b52007-08-12 13:58:50 +0200722 if ((err = device_create_file(dev, &dev_attr_name)))
723 goto error_remove_files;
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200724
Tony Jones1beeffe2007-08-20 13:46:20 -0700725 data->hwmon_dev = hwmon_device_register(dev);
726 if (IS_ERR(data->hwmon_dev)) {
727 err = PTR_ERR(data->hwmon_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200728 goto error_remove_files;
729 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
731 return 0;
732
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200733error_remove_files:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200734 sysfs_remove_group(&dev->kobj, &smsc47m1_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735error_free:
Jean Delvare04a62172007-06-12 13:57:19 +0200736 platform_set_drvdata(pdev, NULL);
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400737 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738error_release:
Jean Delvarea0e92d72009-12-16 21:38:26 +0100739 smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 return err;
741}
742
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100743static int __exit smsc47m1_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200745 struct smsc47m1_data *data = platform_get_drvdata(pdev);
746 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
Tony Jones1beeffe2007-08-20 13:46:20 -0700748 hwmon_device_unregister(data->hwmon_dev);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200749 sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400750
Jean Delvare51f2cca2007-05-08 17:22:00 +0200751 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100752 smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev);
Jean Delvare04a62172007-06-12 13:57:19 +0200753 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400754 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
756 return 0;
757}
758
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
760 int init)
761{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200762 struct smsc47m1_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100764 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
766 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200767 int i, fan_nr;
768 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
Jean Delvare8eccbb62007-05-08 17:21:59 +0200770 for (i = 0; i < fan_nr; i++) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200771 data->fan[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200772 SMSC47M1_REG_FAN[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200773 data->fan_preload[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200774 SMSC47M1_REG_FAN_PRELOAD[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200775 data->pwm[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200776 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 }
778
Jean Delvare51f2cca2007-05-08 17:22:00 +0200779 i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 data->fan_div[0] = (i >> 4) & 0x03;
781 data->fan_div[1] = i >> 6;
782
Jean Delvare51f2cca2007-05-08 17:22:00 +0200783 data->alarms = smsc47m1_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 SMSC47M1_REG_ALARM) >> 6;
785 /* Clear alarms if needed */
786 if (data->alarms)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200787 smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Jean Delvare8eccbb62007-05-08 17:21:59 +0200789 if (fan_nr >= 3) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200790 data->fan_div[2] = (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200791 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200792 data->alarms |= (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200793 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
794 /* Clear alarm if needed */
795 if (data->alarms & 0x04)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200796 smsc47m1_write_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200797 SMSC47M2_REG_ALARM6,
798 0x40);
799 }
800
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 data->last_updated = jiffies;
802 }
803
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100804 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 return data;
806}
807
Jean Delvare51f2cca2007-05-08 17:22:00 +0200808static int __init smsc47m1_device_add(unsigned short address,
809 const struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200811 struct resource res = {
812 .start = address,
813 .end = address + SMSC_EXTENT - 1,
814 .name = DRVNAME,
815 .flags = IORESOURCE_IO,
816 };
817 int err;
818
Jean Delvarea0e92d72009-12-16 21:38:26 +0100819 err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
Jean Delvareb9acb642009-01-07 16:37:35 +0100820 if (err)
821 goto exit;
822
Jean Delvare51f2cca2007-05-08 17:22:00 +0200823 pdev = platform_device_alloc(DRVNAME, address);
824 if (!pdev) {
825 err = -ENOMEM;
826 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
827 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 }
829
Jean Delvare51f2cca2007-05-08 17:22:00 +0200830 err = platform_device_add_resources(pdev, &res, 1);
831 if (err) {
832 printk(KERN_ERR DRVNAME ": Device resource addition failed "
833 "(%d)\n", err);
834 goto exit_device_put;
835 }
836
Jean Delvare2df6d812007-06-09 10:11:16 -0400837 err = platform_device_add_data(pdev, sio_data,
838 sizeof(struct smsc47m1_sio_data));
839 if (err) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200840 printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
841 goto exit_device_put;
842 }
Jean Delvare51f2cca2007-05-08 17:22:00 +0200843
844 err = platform_device_add(pdev);
845 if (err) {
846 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
847 err);
848 goto exit_device_put;
849 }
850
851 return 0;
852
853exit_device_put:
854 platform_device_put(pdev);
855exit:
856 return err;
857}
858
859static int __init sm_smsc47m1_init(void)
860{
861 int err;
862 unsigned short address;
863 struct smsc47m1_sio_data sio_data;
864
865 if (smsc47m1_find(&address, &sio_data))
866 return -ENODEV;
867
Jean Delvare51f2cca2007-05-08 17:22:00 +0200868 /* Sets global pdev as a side effect */
869 err = smsc47m1_device_add(address, &sio_data);
870 if (err)
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100871 goto exit;
872
873 err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
874 if (err)
875 goto exit_device;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200876
877 return 0;
878
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100879exit_device:
880 platform_device_unregister(pdev);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100881 smsc47m1_restore(&sio_data);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200882exit:
883 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884}
885
886static void __exit sm_smsc47m1_exit(void)
887{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200888 platform_driver_unregister(&smsc47m1_driver);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100889 smsc47m1_restore(pdev->dev.platform_data);
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100890 platform_device_unregister(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891}
892
893MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
894MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
895MODULE_LICENSE("GPL");
896
897module_init(sm_smsc47m1_init);
898module_exit(sm_smsc47m1_exit);