blob: aba11e56e8e7b1bfaf092ba64cb7daca4fb3cb3a [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
Guenter Roeck7e612682012-01-16 17:15:02 -0800383static struct attribute *smsc47m1_attributes_fan1[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200384 &sensor_dev_attr_fan1_input.dev_attr.attr,
385 &sensor_dev_attr_fan1_min.dev_attr.attr,
386 &sensor_dev_attr_fan1_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100387 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
Guenter Roeck7e612682012-01-16 17:15:02 -0800388 NULL
389};
390
391static const struct attribute_group smsc47m1_group_fan1 = {
392 .attrs = smsc47m1_attributes_fan1,
393};
394
395static struct attribute *smsc47m1_attributes_fan2[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200396 &sensor_dev_attr_fan2_input.dev_attr.attr,
397 &sensor_dev_attr_fan2_min.dev_attr.attr,
398 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100399 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Guenter Roeck7e612682012-01-16 17:15:02 -0800400 NULL
401};
402
403static const struct attribute_group smsc47m1_group_fan2 = {
404 .attrs = smsc47m1_attributes_fan2,
405};
406
407static struct attribute *smsc47m1_attributes_fan3[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200408 &sensor_dev_attr_fan3_input.dev_attr.attr,
409 &sensor_dev_attr_fan3_min.dev_attr.attr,
410 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100411 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
Guenter Roeck7e612682012-01-16 17:15:02 -0800412 NULL
413};
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200414
Guenter Roeck7e612682012-01-16 17:15:02 -0800415static const struct attribute_group smsc47m1_group_fan3 = {
416 .attrs = smsc47m1_attributes_fan3,
417};
418
419static struct attribute *smsc47m1_attributes_pwm1[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200420 &sensor_dev_attr_pwm1.dev_attr.attr,
421 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
Guenter Roeck7e612682012-01-16 17:15:02 -0800422 NULL
423};
424
425static const struct attribute_group smsc47m1_group_pwm1 = {
426 .attrs = smsc47m1_attributes_pwm1,
427};
428
429static struct attribute *smsc47m1_attributes_pwm2[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200430 &sensor_dev_attr_pwm2.dev_attr.attr,
431 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
Guenter Roeck7e612682012-01-16 17:15:02 -0800432 NULL
433};
434
435static const struct attribute_group smsc47m1_group_pwm2 = {
436 .attrs = smsc47m1_attributes_pwm2,
437};
438
439static struct attribute *smsc47m1_attributes_pwm3[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200440 &sensor_dev_attr_pwm3.dev_attr.attr,
441 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
Guenter Roeck7e612682012-01-16 17:15:02 -0800442 NULL
443};
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200444
Guenter Roeck7e612682012-01-16 17:15:02 -0800445static const struct attribute_group smsc47m1_group_pwm3 = {
446 .attrs = smsc47m1_attributes_pwm3,
447};
448
449static struct attribute *smsc47m1_attributes[] = {
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200450 &dev_attr_alarms.attr,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200451 &dev_attr_name.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200452 NULL
453};
454
455static const struct attribute_group smsc47m1_group = {
456 .attrs = smsc47m1_attributes,
457};
458
Jean Delvare51f2cca2007-05-08 17:22:00 +0200459static int __init smsc47m1_find(unsigned short *addr,
460 struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461{
462 u8 val;
463
464 superio_enter();
Jean Delvare67b671b2007-12-06 23:13:42 +0100465 val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
467 /*
Jean Delvare60917802006-10-08 22:00:44 +0200468 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
469 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200471 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200472 * The LPC47M997 is undocumented, but seems to be compatible with
473 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200474 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
475 * supports a 3rd fan, and the pin configuration registers are
476 * unfortunately different.
Jean Delvare1b54ab42009-07-28 16:31:39 +0200477 * The LPC47M233 has the same device id (0x6B) but is not compatible.
478 * We check the high bit of the device revision register to
479 * differentiate them.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200481 switch (val) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200482 case 0x51:
Joe Perches512504e2010-10-20 06:51:49 +0000483 pr_info("Found SMSC LPC47B27x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200484 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200485 break;
486 case 0x59:
Joe Perches512504e2010-10-20 06:51:49 +0000487 pr_info("Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200488 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200489 break;
490 case 0x5F:
Joe Perches512504e2010-10-20 06:51:49 +0000491 pr_info("Found SMSC LPC47M14x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200492 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200493 break;
494 case 0x60:
Joe Perches512504e2010-10-20 06:51:49 +0000495 pr_info("Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200496 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200497 break;
498 case 0x6B:
Jean Delvare1b54ab42009-07-28 16:31:39 +0200499 if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
Joe Perches512504e2010-10-20 06:51:49 +0000500 pr_debug("Found SMSC LPC47M233, unsupported\n");
Jean Delvare1b54ab42009-07-28 16:31:39 +0200501 superio_exit();
502 return -ENODEV;
503 }
504
Joe Perches512504e2010-10-20 06:51:49 +0000505 pr_info("Found SMSC LPC47M292\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200506 sio_data->type = smsc47m2;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200507 break;
508 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 superio_exit();
510 return -ENODEV;
511 }
512
513 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200514 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
515 | superio_inb(SUPERIO_REG_BASE + 1);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100516 if (*addr == 0) {
Joe Perches512504e2010-10-20 06:51:49 +0000517 pr_info("Device address not set, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 superio_exit();
519 return -ENODEV;
520 }
521
Jean Delvarefa0bff02009-12-16 21:38:27 +0100522 /* Enable only if address is set (needed at least on the
523 * Compaq Presario S4000NX) */
524 sio_data->activate = superio_inb(SUPERIO_REG_ACT);
525 if ((sio_data->activate & 0x01) == 0) {
Joe Perches512504e2010-10-20 06:51:49 +0000526 pr_info("Enabling device\n");
Jean Delvarefa0bff02009-12-16 21:38:27 +0100527 superio_outb(SUPERIO_REG_ACT, sio_data->activate | 0x01);
528 }
529
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 superio_exit();
531 return 0;
532}
533
Jean Delvarefa0bff02009-12-16 21:38:27 +0100534/* Restore device to its initial state */
Jeff Mahoneya00d6432010-01-25 15:00:48 +0100535static void smsc47m1_restore(const struct smsc47m1_sio_data *sio_data)
Jean Delvarefa0bff02009-12-16 21:38:27 +0100536{
537 if ((sio_data->activate & 0x01) == 0) {
538 superio_enter();
539 superio_select();
540
Joe Perches512504e2010-10-20 06:51:49 +0000541 pr_info("Disabling device\n");
Jean Delvarefa0bff02009-12-16 21:38:27 +0100542 superio_outb(SUPERIO_REG_ACT, sio_data->activate);
543
544 superio_exit();
545 }
546}
547
Jean Delvarea0e92d72009-12-16 21:38:26 +0100548#define CHECK 1
549#define REQUEST 2
550#define RELEASE 3
551
552/*
553 * This function can be used to:
554 * - test for resource conflicts with ACPI
555 * - request the resources
556 * - release the resources
557 * We only allocate the I/O ports we really need, to minimize the risk of
558 * conflicts with ACPI or with other drivers.
559 */
560static int smsc47m1_handle_resources(unsigned short address, enum chips type,
561 int action, struct device *dev)
562{
563 static const u8 ports_m1[] = {
564 /* register, region length */
565 0x04, 1,
566 0x33, 4,
567 0x56, 7,
568 };
569
570 static const u8 ports_m2[] = {
571 /* register, region length */
572 0x04, 1,
573 0x09, 1,
574 0x2c, 2,
575 0x35, 4,
576 0x56, 7,
577 0x69, 4,
578 };
579
580 int i, ports_size, err;
581 const u8 *ports;
582
583 switch (type) {
584 case smsc47m1:
585 default:
586 ports = ports_m1;
587 ports_size = ARRAY_SIZE(ports_m1);
588 break;
589 case smsc47m2:
590 ports = ports_m2;
591 ports_size = ARRAY_SIZE(ports_m2);
592 break;
593 }
594
595 for (i = 0; i + 1 < ports_size; i += 2) {
596 unsigned short start = address + ports[i];
597 unsigned short len = ports[i + 1];
598
599 switch (action) {
600 case CHECK:
601 /* Only check for conflicts */
602 err = acpi_check_region(start, len, DRVNAME);
603 if (err)
604 return err;
605 break;
606 case REQUEST:
607 /* Request the resources */
608 if (!request_region(start, len, DRVNAME)) {
609 dev_err(dev, "Region 0x%hx-0x%hx already in "
610 "use!\n", start, start + len);
611
612 /* Undo all requests */
613 for (i -= 2; i >= 0; i -= 2)
614 release_region(address + ports[i],
615 ports[i + 1]);
616 return -EBUSY;
617 }
618 break;
619 case RELEASE:
620 /* Release the resources */
621 release_region(start, len);
622 break;
623 }
624 }
625
626 return 0;
627}
628
Guenter Roeck7e612682012-01-16 17:15:02 -0800629static void smsc47m1_remove_files(struct device *dev)
630{
631 sysfs_remove_group(&dev->kobj, &smsc47m1_group);
632 sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan1);
633 sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan2);
634 sysfs_remove_group(&dev->kobj, &smsc47m1_group_fan3);
635 sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm1);
636 sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm2);
637 sysfs_remove_group(&dev->kobj, &smsc47m1_group_pwm3);
638}
639
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100640static int __init smsc47m1_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200642 struct device *dev = &pdev->dev;
643 struct smsc47m1_sio_data *sio_data = dev->platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 struct smsc47m1_data *data;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200645 struct resource *res;
Jean Delvarea0e92d72009-12-16 21:38:26 +0100646 int err;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200647 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
Jean Delvare51f2cca2007-05-08 17:22:00 +0200649 static const char *names[] = {
650 "smsc47m1",
651 "smsc47m2",
652 };
653
654 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100655 err = smsc47m1_handle_resources(res->start, sio_data->type,
656 REQUEST, dev);
657 if (err < 0)
658 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200660 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 err = -ENOMEM;
662 goto error_release;
663 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Jean Delvare51f2cca2007-05-08 17:22:00 +0200665 data->addr = res->start;
666 data->type = sio_data->type;
667 data->name = names[sio_data->type];
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100668 mutex_init(&data->update_lock);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200669 platform_set_drvdata(pdev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
671 /* If no function is properly configured, there's no point in
672 actually registering the chip. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200673 pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 == 0x04;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200675 pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200677 if (data->type == smsc47m2) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200678 fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200679 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200680 fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200681 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200682 fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200683 & 0x0d) == 0x0d;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200684 pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200685 & 0x0d) == 0x08;
686 } else {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200687 fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200688 & 0x05) == 0x05;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200689 fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200690 & 0x05) == 0x05;
691 fan3 = 0;
692 pwm3 = 0;
693 }
694 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200695 dev_warn(dev, "Device not configured, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 err = -ENODEV;
697 goto error_free;
698 }
699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 /* Some values (fan min, clock dividers, pwm registers) may be
701 needed before any update is triggered, so we better read them
702 at least once here. We don't usually do it that way, but in
703 this particular case, manually reading 5 registers out of 8
704 doesn't make much sense and we're better using the existing
705 function. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200706 smsc47m1_update_device(dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400708 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709 if (fan1) {
Guenter Roeck7e612682012-01-16 17:15:02 -0800710 err = sysfs_create_group(&dev->kobj,
711 &smsc47m1_group_fan1);
712 if (err)
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200713 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200715 dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
717 if (fan2) {
Guenter Roeck7e612682012-01-16 17:15:02 -0800718 err = sysfs_create_group(&dev->kobj,
719 &smsc47m1_group_fan2);
720 if (err)
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200721 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200723 dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
Jean Delvare8eccbb62007-05-08 17:21:59 +0200725 if (fan3) {
Guenter Roeck7e612682012-01-16 17:15:02 -0800726 err = sysfs_create_group(&dev->kobj,
727 &smsc47m1_group_fan3);
728 if (err)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200729 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200730 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200731 dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200732
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 if (pwm1) {
Guenter Roeck7e612682012-01-16 17:15:02 -0800734 err = sysfs_create_group(&dev->kobj,
735 &smsc47m1_group_pwm1);
736 if (err)
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200737 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200739 dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200740
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 if (pwm2) {
Guenter Roeck7e612682012-01-16 17:15:02 -0800742 err = sysfs_create_group(&dev->kobj,
743 &smsc47m1_group_pwm2);
744 if (err)
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200745 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200747 dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Jean Delvare8eccbb62007-05-08 17:21:59 +0200749 if (pwm3) {
Guenter Roeck7e612682012-01-16 17:15:02 -0800750 err = sysfs_create_group(&dev->kobj,
751 &smsc47m1_group_pwm3);
752 if (err)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200753 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200754 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200755 dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200756
Guenter Roeck7e612682012-01-16 17:15:02 -0800757 err = sysfs_create_group(&dev->kobj, &smsc47m1_group);
758 if (err)
Jean Delvare68a50b52007-08-12 13:58:50 +0200759 goto error_remove_files;
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200760
Tony Jones1beeffe2007-08-20 13:46:20 -0700761 data->hwmon_dev = hwmon_device_register(dev);
762 if (IS_ERR(data->hwmon_dev)) {
763 err = PTR_ERR(data->hwmon_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200764 goto error_remove_files;
765 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
767 return 0;
768
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200769error_remove_files:
Guenter Roeck7e612682012-01-16 17:15:02 -0800770 smsc47m1_remove_files(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771error_free:
Jean Delvare04a62172007-06-12 13:57:19 +0200772 platform_set_drvdata(pdev, NULL);
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400773 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774error_release:
Jean Delvarea0e92d72009-12-16 21:38:26 +0100775 smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 return err;
777}
778
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100779static int __exit smsc47m1_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200781 struct smsc47m1_data *data = platform_get_drvdata(pdev);
782 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783
Tony Jones1beeffe2007-08-20 13:46:20 -0700784 hwmon_device_unregister(data->hwmon_dev);
Guenter Roeck7e612682012-01-16 17:15:02 -0800785 smsc47m1_remove_files(&pdev->dev);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400786
Jean Delvare51f2cca2007-05-08 17:22:00 +0200787 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100788 smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev);
Jean Delvare04a62172007-06-12 13:57:19 +0200789 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400790 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
792 return 0;
793}
794
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
796 int init)
797{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200798 struct smsc47m1_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100800 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
802 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200803 int i, fan_nr;
804 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Jean Delvare8eccbb62007-05-08 17:21:59 +0200806 for (i = 0; i < fan_nr; i++) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200807 data->fan[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200808 SMSC47M1_REG_FAN[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200809 data->fan_preload[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200810 SMSC47M1_REG_FAN_PRELOAD[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200811 data->pwm[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200812 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 }
814
Jean Delvare51f2cca2007-05-08 17:22:00 +0200815 i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 data->fan_div[0] = (i >> 4) & 0x03;
817 data->fan_div[1] = i >> 6;
818
Jean Delvare51f2cca2007-05-08 17:22:00 +0200819 data->alarms = smsc47m1_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 SMSC47M1_REG_ALARM) >> 6;
821 /* Clear alarms if needed */
822 if (data->alarms)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200823 smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Jean Delvare8eccbb62007-05-08 17:21:59 +0200825 if (fan_nr >= 3) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200826 data->fan_div[2] = (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200827 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200828 data->alarms |= (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200829 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
830 /* Clear alarm if needed */
831 if (data->alarms & 0x04)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200832 smsc47m1_write_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200833 SMSC47M2_REG_ALARM6,
834 0x40);
835 }
836
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 data->last_updated = jiffies;
838 }
839
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100840 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 return data;
842}
843
Jean Delvare51f2cca2007-05-08 17:22:00 +0200844static int __init smsc47m1_device_add(unsigned short address,
845 const struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200847 struct resource res = {
848 .start = address,
849 .end = address + SMSC_EXTENT - 1,
850 .name = DRVNAME,
851 .flags = IORESOURCE_IO,
852 };
853 int err;
854
Jean Delvarea0e92d72009-12-16 21:38:26 +0100855 err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
Jean Delvareb9acb642009-01-07 16:37:35 +0100856 if (err)
857 goto exit;
858
Jean Delvare51f2cca2007-05-08 17:22:00 +0200859 pdev = platform_device_alloc(DRVNAME, address);
860 if (!pdev) {
861 err = -ENOMEM;
Joe Perches512504e2010-10-20 06:51:49 +0000862 pr_err("Device allocation failed\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200863 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 }
865
Jean Delvare51f2cca2007-05-08 17:22:00 +0200866 err = platform_device_add_resources(pdev, &res, 1);
867 if (err) {
Joe Perches512504e2010-10-20 06:51:49 +0000868 pr_err("Device resource addition failed (%d)\n", err);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200869 goto exit_device_put;
870 }
871
Jean Delvare2df6d812007-06-09 10:11:16 -0400872 err = platform_device_add_data(pdev, sio_data,
873 sizeof(struct smsc47m1_sio_data));
874 if (err) {
Joe Perches512504e2010-10-20 06:51:49 +0000875 pr_err("Platform data allocation failed\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200876 goto exit_device_put;
877 }
Jean Delvare51f2cca2007-05-08 17:22:00 +0200878
879 err = platform_device_add(pdev);
880 if (err) {
Joe Perches512504e2010-10-20 06:51:49 +0000881 pr_err("Device addition failed (%d)\n", err);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200882 goto exit_device_put;
883 }
884
885 return 0;
886
887exit_device_put:
888 platform_device_put(pdev);
889exit:
890 return err;
891}
892
893static int __init sm_smsc47m1_init(void)
894{
895 int err;
896 unsigned short address;
897 struct smsc47m1_sio_data sio_data;
898
899 if (smsc47m1_find(&address, &sio_data))
900 return -ENODEV;
901
Jean Delvare51f2cca2007-05-08 17:22:00 +0200902 /* Sets global pdev as a side effect */
903 err = smsc47m1_device_add(address, &sio_data);
904 if (err)
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100905 goto exit;
906
907 err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
908 if (err)
909 goto exit_device;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200910
911 return 0;
912
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100913exit_device:
914 platform_device_unregister(pdev);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100915 smsc47m1_restore(&sio_data);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200916exit:
917 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918}
919
920static void __exit sm_smsc47m1_exit(void)
921{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200922 platform_driver_unregister(&smsc47m1_driver);
Jean Delvarefa0bff02009-12-16 21:38:27 +0100923 smsc47m1_restore(pdev->dev.platform_data);
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100924 platform_device_unregister(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925}
926
927MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
928MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
929MODULE_LICENSE("GPL");
930
931module_init(sm_smsc47m1_init);
932module_exit(sm_smsc47m1_exit);