blob: f44a89aac381af7f93fb499e3a34d12eb9488ee6 [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
Joe Perches512504e2010-10-20 06:51:49 +000029#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
30
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/module.h>
32#include <linux/slab.h>
33#include <linux/ioport.h>
34#include <linux/jiffies.h>
Jean Delvare51f2cca2007-05-08 17:22:00 +020035#include <linux/platform_device.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040036#include <linux/hwmon.h>
Jean Delvaree84cfbc2007-05-08 17:22:00 +020037#include <linux/hwmon-sysfs.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040038#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/init.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010040#include <linux/mutex.h>
Jean Delvarece8c6ce12006-09-24 21:25:12 +020041#include <linux/sysfs.h>
Jean Delvareb9acb642009-01-07 16:37:35 +010042#include <linux/acpi.h>
H Hartley Sweeten6055fae2009-09-15 17:18:13 +020043#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Jean Delvare67b671b2007-12-06 23:13:42 +010045static unsigned short force_id;
46module_param(force_id, ushort, 0);
47MODULE_PARM_DESC(force_id, "Override the detected device ID");
48
Jean Delvare51f2cca2007-05-08 17:22:00 +020049static struct platform_device *pdev;
50
51#define DRVNAME "smsc47m1"
Jean Delvare8eccbb62007-05-08 17:21:59 +020052enum chips { smsc47m1, smsc47m2 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54/* Super-I/0 registers and commands */
55
56#define REG 0x2e /* The register to read/write */
57#define VAL 0x2f /* The value to read/write */
58
59static inline void
60superio_outb(int reg, int val)
61{
62 outb(reg, REG);
63 outb(val, VAL);
64}
65
66static inline int
67superio_inb(int reg)
68{
69 outb(reg, REG);
70 return inb(VAL);
71}
72
73/* logical device for fans is 0x0A */
74#define superio_select() superio_outb(0x07, 0x0A)
75
76static inline void
77superio_enter(void)
78{
79 outb(0x55, REG);
80}
81
82static inline void
83superio_exit(void)
84{
85 outb(0xAA, REG);
86}
87
88#define SUPERIO_REG_ACT 0x30
89#define SUPERIO_REG_BASE 0x60
90#define SUPERIO_REG_DEVID 0x20
Jean Delvare1b54ab42009-07-28 16:31:39 +020091#define SUPERIO_REG_DEVREV 0x21
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
93/* Logical device registers */
94
95#define SMSC_EXTENT 0x80
96
97/* nr is 0 or 1 in the macros below */
98#define SMSC47M1_REG_ALARM 0x04
99#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
100#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101#define SMSC47M1_REG_FANDIV 0x58
Jean Delvare8eccbb62007-05-08 17:21:59 +0200102
103static const u8 SMSC47M1_REG_FAN[3] = { 0x59, 0x5a, 0x6b };
104static const u8 SMSC47M1_REG_FAN_PRELOAD[3] = { 0x5b, 0x5c, 0x6c };
105static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 };
106
107#define SMSC47M2_REG_ALARM6 0x09
108#define SMSC47M2_REG_TPIN1 0x38
109#define SMSC47M2_REG_TPIN2 0x37
110#define SMSC47M2_REG_TPIN3 0x2d
111#define SMSC47M2_REG_PPIN3 0x2c
112#define SMSC47M2_REG_FANDIV3 0x6a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
114#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
115 983040/((192-(reg))*(div)))
116#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
117 983040/(((reg)-(preload))*(div)))
118#define DIV_FROM_REG(reg) (1 << (reg))
119#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
120#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
121#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
122
123struct smsc47m1_data {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200124 unsigned short addr;
125 const char *name;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200126 enum chips type;
Tony Jones1beeffe2007-08-20 13:46:20 -0700127 struct device *hwmon_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100129 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 unsigned long last_updated; /* In jiffies */
131
Jean Delvare8eccbb62007-05-08 17:21:59 +0200132 u8 fan[3]; /* Register value */
133 u8 fan_preload[3]; /* Register value */
134 u8 fan_div[3]; /* Register encoding, shifted right */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 u8 alarms; /* Register encoding */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200136 u8 pwm[3]; /* Register value (bit 0 is disable) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137};
138
Jean Delvare51f2cca2007-05-08 17:22:00 +0200139struct smsc47m1_sio_data {
140 enum chips type;
Jean Delvarefa0bff02009-12-16 21:38:27 +0100141 u8 activate; /* Remember initial device state */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200142};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
Jean Delvare51f2cca2007-05-08 17:22:00 +0200144
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100145static int __exit smsc47m1_remove(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
147 int init);
148
Jean Delvare51f2cca2007-05-08 17:22:00 +0200149static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg)
Jean Delvare94e183f2007-05-08 17:21:59 +0200150{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200151 return inb_p(data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200152}
153
Jean Delvare51f2cca2007-05-08 17:22:00 +0200154static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg,
Jean Delvare94e183f2007-05-08 17:21:59 +0200155 u8 value)
156{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200157 outb_p(value, data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200158}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
Jean Delvare51f2cca2007-05-08 17:22:00 +0200160static struct platform_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100161 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200162 .owner = THIS_MODULE,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200163 .name = DRVNAME,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100164 },
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100165 .remove = __exit_p(smsc47m1_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166};
167
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200168static ssize_t get_fan(struct device *dev, struct device_attribute
169 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200171 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200173 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 /* This chip (stupidly) stops monitoring fan speed if PWM is
175 enabled and duty cycle is 0%. This is fine if the monitoring
176 and control concern the same fan, but troublesome if they are
177 not (which could as well happen). */
178 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
179 FAN_FROM_REG(data->fan[nr],
180 DIV_FROM_REG(data->fan_div[nr]),
181 data->fan_preload[nr]);
182 return sprintf(buf, "%d\n", rpm);
183}
184
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200185static ssize_t get_fan_min(struct device *dev, struct device_attribute
186 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200188 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200190 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 int rpm = MIN_FROM_REG(data->fan_preload[nr],
192 DIV_FROM_REG(data->fan_div[nr]));
193 return sprintf(buf, "%d\n", rpm);
194}
195
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200196static ssize_t get_fan_div(struct device *dev, struct device_attribute
197 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200199 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200201 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202}
203
Jean Delvare1f08af72008-01-06 15:36:13 +0100204static ssize_t get_fan_alarm(struct device *dev, struct device_attribute
205 *devattr, char *buf)
206{
207 int bitnr = to_sensor_dev_attr(devattr)->index;
208 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
209 return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
210}
211
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200212static ssize_t get_pwm(struct device *dev, struct device_attribute
213 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200215 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200217 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218}
219
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200220static ssize_t get_pwm_en(struct device *dev, struct device_attribute
221 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200223 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200225 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226}
227
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200228static ssize_t get_alarms(struct device *dev, struct device_attribute
229 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
231 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
232 return sprintf(buf, "%d\n", data->alarms);
233}
234
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200235static ssize_t set_fan_min(struct device *dev, struct device_attribute
236 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200238 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200239 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200240 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 long rpmdiv, val = simple_strtol(buf, NULL, 10);
242
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100243 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
245
246 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100247 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 return -EINVAL;
249 }
250
251 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200252 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100254 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
256 return count;
257}
258
259/* Note: we save and restore the fan minimum here, because its value is
260 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200261 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 because the divider changed. */
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200263static ssize_t set_fan_div(struct device *dev, struct device_attribute
264 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200266 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200267 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200268 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 long new_div = simple_strtol(buf, NULL, 10), tmp;
270 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
271
272 if (new_div == old_div) /* No change */
273 return count;
274
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100275 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 switch (new_div) {
277 case 1: data->fan_div[nr] = 0; break;
278 case 2: data->fan_div[nr] = 1; break;
279 case 4: data->fan_div[nr] = 2; break;
280 case 8: data->fan_div[nr] = 3; break;
281 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100282 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 return -EINVAL;
284 }
285
Jean Delvare8eccbb62007-05-08 17:21:59 +0200286 switch (nr) {
287 case 0:
288 case 1:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200289 tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200290 & ~(0x03 << (4 + 2 * nr));
291 tmp |= data->fan_div[nr] << (4 + 2 * nr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200292 smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200293 break;
294 case 2:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200295 tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200296 tmp |= data->fan_div[2] << 4;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200297 smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200298 break;
299 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
301 /* Preserve fan min */
302 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
303 + new_div / 2) / new_div;
304 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200305 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100307 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 return count;
310}
311
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200312static ssize_t set_pwm(struct device *dev, struct device_attribute
313 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200315 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200316 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200317 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 long val = simple_strtol(buf, NULL, 10);
319
320 if (val < 0 || val > 255)
321 return -EINVAL;
322
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100323 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 data->pwm[nr] &= 0x81; /* Preserve additional bits */
325 data->pwm[nr] |= PWM_TO_REG(val);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200326 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100328 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
330 return count;
331}
332
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200333static ssize_t set_pwm_en(struct device *dev, struct device_attribute
334 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200336 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200337 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200338 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 long val = simple_strtol(buf, NULL, 10);
340
341 if (val != 0 && val != 1)
342 return -EINVAL;
343
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100344 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 data->pwm[nr] &= 0xFE; /* preserve the other bits */
346 data->pwm[nr] |= !val;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200347 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100349 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351 return count;
352}
353
354#define fan_present(offset) \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200355static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan, \
356 NULL, offset - 1); \
357static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
358 get_fan_min, set_fan_min, offset - 1); \
359static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
360 get_fan_div, set_fan_div, offset - 1); \
Jean Delvare1f08af72008-01-06 15:36:13 +0100361static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm, \
362 NULL, offset - 1); \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200363static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
364 get_pwm, set_pwm, offset - 1); \
365static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
366 get_pwm_en, set_pwm_en, offset - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367
368fan_present(1);
369fan_present(2);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200370fan_present(3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
373
Jean Delvare51f2cca2007-05-08 17:22:00 +0200374static ssize_t show_name(struct device *dev, struct device_attribute
375 *devattr, char *buf)
376{
377 struct smsc47m1_data *data = dev_get_drvdata(dev);
378
379 return sprintf(buf, "%s\n", data->name);
380}
381static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
382
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200383/* Almost all sysfs files may or may not be created depending on the chip
384 setup so we create them individually. It is still convenient to define a
385 group to remove them all at once. */
386static struct attribute *smsc47m1_attributes[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200387 &sensor_dev_attr_fan1_input.dev_attr.attr,
388 &sensor_dev_attr_fan1_min.dev_attr.attr,
389 &sensor_dev_attr_fan1_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100390 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200391 &sensor_dev_attr_fan2_input.dev_attr.attr,
392 &sensor_dev_attr_fan2_min.dev_attr.attr,
393 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100394 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200395 &sensor_dev_attr_fan3_input.dev_attr.attr,
396 &sensor_dev_attr_fan3_min.dev_attr.attr,
397 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100398 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200399
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200400 &sensor_dev_attr_pwm1.dev_attr.attr,
401 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
402 &sensor_dev_attr_pwm2.dev_attr.attr,
403 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
404 &sensor_dev_attr_pwm3.dev_attr.attr,
405 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200406
407 &dev_attr_alarms.attr,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200408 &dev_attr_name.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200409 NULL
410};
411
412static const struct attribute_group smsc47m1_group = {
413 .attrs = smsc47m1_attributes,
414};
415
Jean Delvare51f2cca2007-05-08 17:22:00 +0200416static int __init smsc47m1_find(unsigned short *addr,
417 struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418{
419 u8 val;
420
421 superio_enter();
Jean Delvare67b671b2007-12-06 23:13:42 +0100422 val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
424 /*
Jean Delvare60917802006-10-08 22:00:44 +0200425 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
426 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200428 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200429 * The LPC47M997 is undocumented, but seems to be compatible with
430 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200431 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
432 * supports a 3rd fan, and the pin configuration registers are
433 * unfortunately different.
Jean Delvare1b54ab42009-07-28 16:31:39 +0200434 * The LPC47M233 has the same device id (0x6B) but is not compatible.
435 * We check the high bit of the device revision register to
436 * differentiate them.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200438 switch (val) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200439 case 0x51:
Joe Perches512504e2010-10-20 06:51:49 +0000440 pr_info("Found SMSC LPC47B27x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200441 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200442 break;
443 case 0x59:
Joe Perches512504e2010-10-20 06:51:49 +0000444 pr_info("Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200445 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200446 break;
447 case 0x5F:
Joe Perches512504e2010-10-20 06:51:49 +0000448 pr_info("Found SMSC LPC47M14x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200449 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200450 break;
451 case 0x60:
Joe Perches512504e2010-10-20 06:51:49 +0000452 pr_info("Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200453 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200454 break;
455 case 0x6B:
Jean Delvare1b54ab42009-07-28 16:31:39 +0200456 if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
Joe Perches512504e2010-10-20 06:51:49 +0000457 pr_debug("Found SMSC LPC47M233, unsupported\n");
Jean Delvare1b54ab42009-07-28 16:31:39 +0200458 superio_exit();
459 return -ENODEV;
460 }
461
Joe Perches512504e2010-10-20 06:51:49 +0000462 pr_info("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);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100473 if (*addr == 0) {
Joe Perches512504e2010-10-20 06:51:49 +0000474 pr_info("Device address not set, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 superio_exit();
476 return -ENODEV;
477 }
478
Jean Delvarefa0bff02009-12-16 21:38:27 +0100479 /* Enable only if address is set (needed at least on the
480 * Compaq Presario S4000NX) */
481 sio_data->activate = superio_inb(SUPERIO_REG_ACT);
482 if ((sio_data->activate & 0x01) == 0) {
Joe Perches512504e2010-10-20 06:51:49 +0000483 pr_info("Enabling device\n");
Jean Delvarefa0bff02009-12-16 21:38:27 +0100484 superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
485 }
486
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 superio_exit();
488 return 0;
489}
490
Jean Delvarefa0bff02009-12-16 21:38:27 +0100491/* Restore device to its initial state */
Jeff Mahoneya00d6432010-01-25 15:00:48 +0100492static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
Jean Delvarefa0bff02009-12-16 21:38:27 +0100493{
494 if ((sio_data->activate & 0x01) == 0) {
495 superio_enter();
496 superio_select();
497
Joe Perches512504e2010-10-20 06:51:49 +0000498 pr_info("Disabling device\n");
Jean Delvarefa0bff02009-12-16 21:38:27 +0100499 superio_outb(SUPERIO_REG_ACT, sio_data->activate);
500
501 superio_exit();
502 }
503}
504
Jean Delvarea0e92d72009-12-16 21:38:26 +0100505#define CHECK 1
506#define REQUEST 2
507#define RELEASE 3
508
509/*
510 * This function can be used to:
511 * - test for resource conflicts with ACPI
512 * - request the resources
513 * - release the resources
514 * We only allocate the I/O ports we really need, to minimize the risk of
515 * conflicts with ACPI or with other drivers.
516 */
517static int smsc47m1_handle_resources(unsigned short address, enum chips type,
518 int action, struct device *dev)
519{
520 static const u8 ports_m1[] = {
521 /* register, region length */
522 0x04, 1,
523 0x33, 4,
524 0x56, 7,
525 };
526
527 static const u8 ports_m2[] = {
528 /* register, region length */
529 0x04, 1,
530 0x09, 1,
531 0x2c, 2,
532 0x35, 4,
533 0x56, 7,
534 0x69, 4,
535 };
536
537 int i, ports_size, err;
538 const u8 *ports;
539
540 switch (type) {
541 case smsc47m1:
542 default:
543 ports = ports_m1;
544 ports_size = ARRAY_SIZE(ports_m1);
545 break;
546 case smsc47m2:
547 ports = ports_m2;
548 ports_size = ARRAY_SIZE(ports_m2);
549 break;
550 }
551
552 for (i = 0; i + 1 < ports_size; i += 2) {
553 unsigned short start = address + ports[i];
554 unsigned short len = ports[i + 1];
555
556 switch (action) {
557 case CHECK:
558 /* Only check for conflicts */
559 err = acpi_check_region(start, len, DRVNAME);
560 if (err)
561 return err;
562 break;
563 case REQUEST:
564 /* Request the resources */
565 if (!request_region(start, len, DRVNAME)) {
566 dev_err(dev, "Region 0x%hx-0x%hx already in "
567 "use!\n", start, start + len);
568
569 /* Undo all requests */
570 for (i -= 2; i >= 0; i -= 2)
571 release_region(address + ports[i],
572 ports[i + 1]);
573 return -EBUSY;
574 }
575 break;
576 case RELEASE:
577 /* Release the resources */
578 release_region(start, len);
579 break;
580 }
581 }
582
583 return 0;
584}
585
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100586static int __init smsc47m1_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200588 struct device *dev = &pdev->dev;
589 struct smsc47m1_sio_data *sio_data = dev->platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 struct smsc47m1_data *data;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200591 struct resource *res;
Jean Delvarea0e92d72009-12-16 21:38:26 +0100592 int err;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200593 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
Jean Delvare51f2cca2007-05-08 17:22:00 +0200595 static const char *names[] = {
596 "smsc47m1",
597 "smsc47m2",
598 };
599
600 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100601 err = smsc47m1_handle_resources(res->start, sio_data->type,
602 REQUEST, dev);
603 if (err < 0)
604 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200606 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 err = -ENOMEM;
608 goto error_release;
609 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
Jean Delvare51f2cca2007-05-08 17:22:00 +0200611 data->addr = res->start;
612 data->type = sio_data->type;
613 data->name = names[sio_data->type];
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100614 mutex_init(&data->update_lock);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200615 platform_set_drvdata(pdev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616
617 /* If no function is properly configured, there's no point in
618 actually registering the chip. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200619 pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 == 0x04;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200621 pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200623 if (data->type == smsc47m2) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200624 fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200625 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200626 fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200627 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200628 fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200629 & 0x0d) == 0x0d;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200630 pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200631 & 0x0d) == 0x08;
632 } else {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200633 fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200634 & 0x05) == 0x05;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200635 fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200636 & 0x05) == 0x05;
637 fan3 = 0;
638 pwm3 = 0;
639 }
640 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200641 dev_warn(dev, "Device not configured, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 err = -ENODEV;
643 goto error_free;
644 }
645
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 /* Some values (fan min, clock dividers, pwm registers) may be
647 needed before any update is triggered, so we better read them
648 at least once here. We don't usually do it that way, but in
649 this particular case, manually reading 5 registers out of 8
650 doesn't make much sense and we're better using the existing
651 function. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200652 smsc47m1_update_device(dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400654 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 if (fan1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200656 if ((err = device_create_file(dev,
657 &sensor_dev_attr_fan1_input.dev_attr))
658 || (err = device_create_file(dev,
659 &sensor_dev_attr_fan1_min.dev_attr))
660 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100661 &sensor_dev_attr_fan1_div.dev_attr))
662 || (err = device_create_file(dev,
663 &sensor_dev_attr_fan1_alarm.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200664 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200666 dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
668 if (fan2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200669 if ((err = device_create_file(dev,
670 &sensor_dev_attr_fan2_input.dev_attr))
671 || (err = device_create_file(dev,
672 &sensor_dev_attr_fan2_min.dev_attr))
673 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100674 &sensor_dev_attr_fan2_div.dev_attr))
675 || (err = device_create_file(dev,
676 &sensor_dev_attr_fan2_alarm.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200677 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200679 dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Jean Delvare8eccbb62007-05-08 17:21:59 +0200681 if (fan3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200682 if ((err = device_create_file(dev,
683 &sensor_dev_attr_fan3_input.dev_attr))
684 || (err = device_create_file(dev,
685 &sensor_dev_attr_fan3_min.dev_attr))
686 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100687 &sensor_dev_attr_fan3_div.dev_attr))
688 || (err = device_create_file(dev,
689 &sensor_dev_attr_fan3_alarm.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200690 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200691 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200692 dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200693
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 if (pwm1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200695 if ((err = device_create_file(dev,
696 &sensor_dev_attr_pwm1.dev_attr))
697 || (err = device_create_file(dev,
698 &sensor_dev_attr_pwm1_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200699 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200701 dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200702
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 if (pwm2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200704 if ((err = device_create_file(dev,
705 &sensor_dev_attr_pwm2.dev_attr))
706 || (err = device_create_file(dev,
707 &sensor_dev_attr_pwm2_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200708 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200710 dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
Jean Delvare8eccbb62007-05-08 17:21:59 +0200712 if (pwm3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200713 if ((err = device_create_file(dev,
714 &sensor_dev_attr_pwm3.dev_attr))
715 || (err = device_create_file(dev,
716 &sensor_dev_attr_pwm3_enable.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200717 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200718 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200719 dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200720
Jean Delvare51f2cca2007-05-08 17:22:00 +0200721 if ((err = device_create_file(dev, &dev_attr_alarms)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200722 goto error_remove_files;
Jean Delvare68a50b52007-08-12 13:58:50 +0200723 if ((err = device_create_file(dev, &dev_attr_name)))
724 goto error_remove_files;
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200725
Tony Jones1beeffe2007-08-20 13:46:20 -0700726 data->hwmon_dev = hwmon_device_register(dev);
727 if (IS_ERR(data->hwmon_dev)) {
728 err = PTR_ERR(data->hwmon_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200729 goto error_remove_files;
730 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731
732 return 0;
733
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200734error_remove_files:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200735 sysfs_remove_group(&dev->kobj, &smsc47m1_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736error_free:
Jean Delvare04a62172007-06-12 13:57:19 +0200737 platform_set_drvdata(pdev, NULL);
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400738 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739error_release:
Jean Delvarea0e92d72009-12-16 21:38:26 +0100740 smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 return err;
742}
743
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100744static int __exit smsc47m1_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200746 struct smsc47m1_data *data = platform_get_drvdata(pdev);
747 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Tony Jones1beeffe2007-08-20 13:46:20 -0700749 hwmon_device_unregister(data->hwmon_dev);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200750 sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400751
Jean Delvare51f2cca2007-05-08 17:22:00 +0200752 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100753 smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev);
Jean Delvare04a62172007-06-12 13:57:19 +0200754 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400755 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
757 return 0;
758}
759
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
761 int init)
762{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200763 struct smsc47m1_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100765 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
767 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200768 int i, fan_nr;
769 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Jean Delvare8eccbb62007-05-08 17:21:59 +0200771 for (i = 0; i < fan_nr; i++) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200772 data->fan[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200773 SMSC47M1_REG_FAN[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200774 data->fan_preload[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200775 SMSC47M1_REG_FAN_PRELOAD[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200776 data->pwm[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200777 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 }
779
Jean Delvare51f2cca2007-05-08 17:22:00 +0200780 i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 data->fan_div[0] = (i >> 4) & 0x03;
782 data->fan_div[1] = i >> 6;
783
Jean Delvare51f2cca2007-05-08 17:22:00 +0200784 data->alarms = smsc47m1_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 SMSC47M1_REG_ALARM) >> 6;
786 /* Clear alarms if needed */
787 if (data->alarms)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200788 smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
Jean Delvare8eccbb62007-05-08 17:21:59 +0200790 if (fan_nr >= 3) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200791 data->fan_div[2] = (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200792 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200793 data->alarms |= (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200794 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
795 /* Clear alarm if needed */
796 if (data->alarms & 0x04)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200797 smsc47m1_write_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200798 SMSC47M2_REG_ALARM6,
799 0x40);
800 }
801
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 data->last_updated = jiffies;
803 }
804
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100805 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 return data;
807}
808
Jean Delvare51f2cca2007-05-08 17:22:00 +0200809static int __init smsc47m1_device_add(unsigned short address,
810 const struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200812 struct resource res = {
813 .start = address,
814 .end = address + SMSC_EXTENT - 1,
815 .name = DRVNAME,
816 .flags = IORESOURCE_IO,
817 };
818 int err;
819
Jean Delvarea0e92d72009-12-16 21:38:26 +0100820 err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
Jean Delvareb9acb642009-01-07 16:37:35 +0100821 if (err)
822 goto exit;
823
Jean Delvare51f2cca2007-05-08 17:22:00 +0200824 pdev = platform_device_alloc(DRVNAME, address);
825 if (!pdev) {
826 err = -ENOMEM;
Joe Perches512504e2010-10-20 06:51:49 +0000827 pr_err("Device allocation failed\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200828 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 }
830
Jean Delvare51f2cca2007-05-08 17:22:00 +0200831 err = platform_device_add_resources(pdev, &res, 1);
832 if (err) {
Joe Perches512504e2010-10-20 06:51:49 +0000833 pr_err("Device resource addition failed (%d)\n", err);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200834 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) {
Joe Perches512504e2010-10-20 06:51:49 +0000840 pr_err("Platform data allocation failed\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200841 goto exit_device_put;
842 }
Jean Delvare51f2cca2007-05-08 17:22:00 +0200843
844 err = platform_device_add(pdev);
845 if (err) {
Joe Perches512504e2010-10-20 06:51:49 +0000846 pr_err("Device addition failed (%d)\n", err);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200847 goto exit_device_put;
848 }
849
850 return 0;
851
852exit_device_put:
853 platform_device_put(pdev);
854exit:
855 return err;
856}
857
858static int __init sm_smsc47m1_init(void)
859{
860 int err;
861 unsigned short address;
862 struct smsc47m1_sio_data sio_data;
863
864 if (smsc47m1_find(&address, &sio_data))
865 return -ENODEV;
866
Jean Delvare51f2cca2007-05-08 17:22:00 +0200867 /* Sets global pdev as a side effect */
868 err = smsc47m1_device_add(address, &sio_data);
869 if (err)
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100870 goto exit;
871
872 err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
873 if (err)
874 goto exit_device;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200875
876 return 0;
877
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100878exit_device:
879 platform_device_unregister(pdev);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100880 smsc47m1_restore(&sio_data);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200881exit:
882 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883}
884
885static void __exit sm_smsc47m1_exit(void)
886{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200887 platform_driver_unregister(&smsc47m1_driver);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100888 smsc47m1_restore(pdev->dev.platform_data);
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100889 platform_device_unregister(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890}
891
892MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
893MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
894MODULE_LICENSE("GPL");
895
896module_init(sm_smsc47m1_init);
897module_exit(sm_smsc47m1_exit);