blob: 92cca512b38eb61f625dd808a36ca54356e08b67 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 smsc47m1.c - Part of lm_sensors, Linux kernel modules
3 for hardware monitoring
4
Jean Delvare60917802006-10-08 22:00:44 +02005 Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x,
Jean Delvare8eccbb62007-05-08 17:21:59 +02006 LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997
7 Super-I/O chips.
Linus Torvalds1da177e2005-04-16 15:20:36 -07008
9 Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
Jean Delvare8eccbb62007-05-08 17:21:59 +020010 Copyright (C) 2004-2007 Jean Delvare <khali@linux-fr.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
12 and Jean Delvare
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27*/
28
29#include <linux/module.h>
30#include <linux/slab.h>
31#include <linux/ioport.h>
32#include <linux/jiffies.h>
Jean Delvare51f2cca2007-05-08 17:22:00 +020033#include <linux/platform_device.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040034#include <linux/hwmon.h>
Jean Delvaree84cfbc2007-05-08 17:22:00 +020035#include <linux/hwmon-sysfs.h>
Mark M. Hoffman943b0832005-07-15 21:39:18 -040036#include <linux/err.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
Ingo Molnar9a61bf62006-01-18 23:19:26 +010038#include <linux/mutex.h>
Jean Delvarece8c6ce12006-09-24 21:25:12 +020039#include <linux/sysfs.h>
Jean Delvareb9acb642009-01-07 16:37:35 +010040#include <linux/acpi.h>
H Hartley Sweeten6055fae2009-09-15 17:18:13 +020041#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Jean Delvare67b671b2007-12-06 23:13:42 +010043static unsigned short force_id;
44module_param(force_id, ushort, 0);
45MODULE_PARM_DESC(force_id, "Override the detected device ID");
46
Jean Delvare51f2cca2007-05-08 17:22:00 +020047static struct platform_device *pdev;
48
49#define DRVNAME "smsc47m1"
Jean Delvare8eccbb62007-05-08 17:21:59 +020050enum chips { smsc47m1, smsc47m2 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52/* Super-I/0 registers and commands */
53
54#define REG 0x2e /* The register to read/write */
55#define VAL 0x2f /* The value to read/write */
56
57static inline void
58superio_outb(int reg, int val)
59{
60 outb(reg, REG);
61 outb(val, VAL);
62}
63
64static inline int
65superio_inb(int reg)
66{
67 outb(reg, REG);
68 return inb(VAL);
69}
70
71/* logical device for fans is 0x0A */
72#define superio_select() superio_outb(0x07, 0x0A)
73
74static inline void
75superio_enter(void)
76{
77 outb(0x55, REG);
78}
79
80static inline void
81superio_exit(void)
82{
83 outb(0xAA, REG);
84}
85
86#define SUPERIO_REG_ACT 0x30
87#define SUPERIO_REG_BASE 0x60
88#define SUPERIO_REG_DEVID 0x20
Jean Delvare1b54ab42009-07-28 16:31:39 +020089#define SUPERIO_REG_DEVREV 0x21
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
91/* Logical device registers */
92
93#define SMSC_EXTENT 0x80
94
95/* nr is 0 or 1 in the macros below */
96#define SMSC47M1_REG_ALARM 0x04
97#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
98#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070099#define SMSC47M1_REG_FANDIV 0x58
Jean Delvare8eccbb62007-05-08 17:21:59 +0200100
101static const u8 SMSC47M1_REG_FAN[3] = { 0x59, 0x5a, 0x6b };
102static const u8 SMSC47M1_REG_FAN_PRELOAD[3] = { 0x5b, 0x5c, 0x6c };
103static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 };
104
105#define SMSC47M2_REG_ALARM6 0x09
106#define SMSC47M2_REG_TPIN1 0x38
107#define SMSC47M2_REG_TPIN2 0x37
108#define SMSC47M2_REG_TPIN3 0x2d
109#define SMSC47M2_REG_PPIN3 0x2c
110#define SMSC47M2_REG_FANDIV3 0x6a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
112#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
113 983040/((192-(reg))*(div)))
114#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
115 983040/(((reg)-(preload))*(div)))
116#define DIV_FROM_REG(reg) (1 << (reg))
117#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
118#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
119#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
120
121struct smsc47m1_data {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200122 unsigned short addr;
123 const char *name;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200124 enum chips type;
Tony Jones1beeffe2007-08-20 13:46:20 -0700125 struct device *hwmon_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100127 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 unsigned long last_updated; /* In jiffies */
129
Jean Delvare8eccbb62007-05-08 17:21:59 +0200130 u8 fan[3]; /* Register value */
131 u8 fan_preload[3]; /* Register value */
132 u8 fan_div[3]; /* Register encoding, shifted right */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 u8 alarms; /* Register encoding */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200134 u8 pwm[3]; /* Register value (bit 0 is disable) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135};
136
Jean Delvare51f2cca2007-05-08 17:22:00 +0200137struct smsc47m1_sio_data {
138 enum chips type;
139};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140
Jean Delvare51f2cca2007-05-08 17:22:00 +0200141
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100142static int __exit smsc47m1_remove(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
144 int init);
145
Jean Delvare51f2cca2007-05-08 17:22:00 +0200146static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg)
Jean Delvare94e183f2007-05-08 17:21:59 +0200147{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200148 return inb_p(data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200149}
150
Jean Delvare51f2cca2007-05-08 17:22:00 +0200151static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg,
Jean Delvare94e183f2007-05-08 17:21:59 +0200152 u8 value)
153{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200154 outb_p(value, data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200155}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
Jean Delvare51f2cca2007-05-08 17:22:00 +0200157static struct platform_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100158 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200159 .owner = THIS_MODULE,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200160 .name = DRVNAME,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100161 },
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100162 .remove = __exit_p(smsc47m1_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163};
164
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200165static ssize_t get_fan(struct device *dev, struct device_attribute
166 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200168 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200170 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 /* This chip (stupidly) stops monitoring fan speed if PWM is
172 enabled and duty cycle is 0%. This is fine if the monitoring
173 and control concern the same fan, but troublesome if they are
174 not (which could as well happen). */
175 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
176 FAN_FROM_REG(data->fan[nr],
177 DIV_FROM_REG(data->fan_div[nr]),
178 data->fan_preload[nr]);
179 return sprintf(buf, "%d\n", rpm);
180}
181
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200182static ssize_t get_fan_min(struct device *dev, struct device_attribute
183 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200185 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200187 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 int rpm = MIN_FROM_REG(data->fan_preload[nr],
189 DIV_FROM_REG(data->fan_div[nr]));
190 return sprintf(buf, "%d\n", rpm);
191}
192
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200193static ssize_t get_fan_div(struct device *dev, struct device_attribute
194 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200196 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200198 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199}
200
Jean Delvare1f08af72008-01-06 15:36:13 +0100201static ssize_t get_fan_alarm(struct device *dev, struct device_attribute
202 *devattr, char *buf)
203{
204 int bitnr = to_sensor_dev_attr(devattr)->index;
205 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
206 return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
207}
208
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200209static ssize_t get_pwm(struct device *dev, struct device_attribute
210 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200212 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200214 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215}
216
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200217static ssize_t get_pwm_en(struct device *dev, struct device_attribute
218 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200220 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200222 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223}
224
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200225static ssize_t get_alarms(struct device *dev, struct device_attribute
226 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227{
228 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
229 return sprintf(buf, "%d\n", data->alarms);
230}
231
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200232static ssize_t set_fan_min(struct device *dev, struct device_attribute
233 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200235 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200236 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200237 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 long rpmdiv, val = simple_strtol(buf, NULL, 10);
239
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100240 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
242
243 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100244 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 return -EINVAL;
246 }
247
248 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200249 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100251 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
253 return count;
254}
255
256/* Note: we save and restore the fan minimum here, because its value is
257 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200258 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 because the divider changed. */
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200260static ssize_t set_fan_div(struct device *dev, struct device_attribute
261 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200263 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200264 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200265 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 long new_div = simple_strtol(buf, NULL, 10), tmp;
267 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
268
269 if (new_div == old_div) /* No change */
270 return count;
271
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100272 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 switch (new_div) {
274 case 1: data->fan_div[nr] = 0; break;
275 case 2: data->fan_div[nr] = 1; break;
276 case 4: data->fan_div[nr] = 2; break;
277 case 8: data->fan_div[nr] = 3; break;
278 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100279 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 return -EINVAL;
281 }
282
Jean Delvare8eccbb62007-05-08 17:21:59 +0200283 switch (nr) {
284 case 0:
285 case 1:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200286 tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200287 & ~(0x03 << (4 + 2 * nr));
288 tmp |= data->fan_div[nr] << (4 + 2 * nr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200289 smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200290 break;
291 case 2:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200292 tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200293 tmp |= data->fan_div[2] << 4;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200294 smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200295 break;
296 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297
298 /* Preserve fan min */
299 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
300 + new_div / 2) / new_div;
301 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200302 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100304 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
306 return count;
307}
308
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200309static ssize_t set_pwm(struct device *dev, struct device_attribute
310 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200312 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200313 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200314 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 long val = simple_strtol(buf, NULL, 10);
316
317 if (val < 0 || val > 255)
318 return -EINVAL;
319
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100320 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 data->pwm[nr] &= 0x81; /* Preserve additional bits */
322 data->pwm[nr] |= PWM_TO_REG(val);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200323 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100325 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326
327 return count;
328}
329
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200330static ssize_t set_pwm_en(struct device *dev, struct device_attribute
331 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200333 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200334 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200335 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 long val = simple_strtol(buf, NULL, 10);
337
338 if (val != 0 && val != 1)
339 return -EINVAL;
340
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100341 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 data->pwm[nr] &= 0xFE; /* preserve the other bits */
343 data->pwm[nr] |= !val;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200344 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100346 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
348 return count;
349}
350
351#define fan_present(offset) \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200352static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan, \
353 NULL, offset - 1); \
354static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
355 get_fan_min, set_fan_min, offset - 1); \
356static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
357 get_fan_div, set_fan_div, offset - 1); \
Jean Delvare1f08af72008-01-06 15:36:13 +0100358static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm, \
359 NULL, offset - 1); \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200360static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
361 get_pwm, set_pwm, offset - 1); \
362static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
363 get_pwm_en, set_pwm_en, offset - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364
365fan_present(1);
366fan_present(2);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200367fan_present(3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
369static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
370
Jean Delvare51f2cca2007-05-08 17:22:00 +0200371static ssize_t show_name(struct device *dev, struct device_attribute
372 *devattr, char *buf)
373{
374 struct smsc47m1_data *data = dev_get_drvdata(dev);
375
376 return sprintf(buf, "%s\n", data->name);
377}
378static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
379
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200380/* Almost all sysfs files may or may not be created depending on the chip
381 setup so we create them individually. It is still convenient to define a
382 group to remove them all at once. */
383static struct attribute *smsc47m1_attributes[] = {
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,
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200388 &sensor_dev_attr_fan2_input.dev_attr.attr,
389 &sensor_dev_attr_fan2_min.dev_attr.attr,
390 &sensor_dev_attr_fan2_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100391 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200392 &sensor_dev_attr_fan3_input.dev_attr.attr,
393 &sensor_dev_attr_fan3_min.dev_attr.attr,
394 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvare1f08af72008-01-06 15:36:13 +0100395 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200396
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200397 &sensor_dev_attr_pwm1.dev_attr.attr,
398 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
399 &sensor_dev_attr_pwm2.dev_attr.attr,
400 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
401 &sensor_dev_attr_pwm3.dev_attr.attr,
402 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200403
404 &dev_attr_alarms.attr,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200405 &dev_attr_name.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200406 NULL
407};
408
409static const struct attribute_group smsc47m1_group = {
410 .attrs = smsc47m1_attributes,
411};
412
Jean Delvare51f2cca2007-05-08 17:22:00 +0200413static int __init smsc47m1_find(unsigned short *addr,
414 struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415{
416 u8 val;
417
418 superio_enter();
Jean Delvare67b671b2007-12-06 23:13:42 +0100419 val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
421 /*
Jean Delvare60917802006-10-08 22:00:44 +0200422 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
423 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200425 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200426 * The LPC47M997 is undocumented, but seems to be compatible with
427 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200428 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
429 * supports a 3rd fan, and the pin configuration registers are
430 * unfortunately different.
Jean Delvare1b54ab42009-07-28 16:31:39 +0200431 * The LPC47M233 has the same device id (0x6B) but is not compatible.
432 * We check the high bit of the device revision register to
433 * differentiate them.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200435 switch (val) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200436 case 0x51:
Jean Delvare620100c2007-05-08 17:22:00 +0200437 pr_info(DRVNAME ": Found SMSC LPC47B27x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200438 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200439 break;
440 case 0x59:
Jean Delvare620100c2007-05-08 17:22:00 +0200441 pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200442 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200443 break;
444 case 0x5F:
Jean Delvare620100c2007-05-08 17:22:00 +0200445 pr_info(DRVNAME ": Found SMSC LPC47M14x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200446 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200447 break;
448 case 0x60:
Jean Delvare620100c2007-05-08 17:22:00 +0200449 pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200450 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200451 break;
452 case 0x6B:
Jean Delvare1b54ab42009-07-28 16:31:39 +0200453 if (superio_inb(SUPERIO_REG_DEVREV) & 0x80) {
454 pr_debug(DRVNAME ": "
455 "Found SMSC LPC47M233, unsupported\n");
456 superio_exit();
457 return -ENODEV;
458 }
459
Jean Delvare620100c2007-05-08 17:22:00 +0200460 pr_info(DRVNAME ": Found SMSC LPC47M292\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200461 sio_data->type = smsc47m2;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200462 break;
463 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 superio_exit();
465 return -ENODEV;
466 }
467
468 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200469 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
470 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200472 if (*addr == 0 || (val & 0x01) == 0) {
Jean Delvare620100c2007-05-08 17:22:00 +0200473 pr_info(DRVNAME ": Device is disabled, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 superio_exit();
475 return -ENODEV;
476 }
477
478 superio_exit();
479 return 0;
480}
481
Jean Delvarea0e92d72009-12-16 21:38:26 +0100482#define CHECK 1
483#define REQUEST 2
484#define RELEASE 3
485
486/*
487 * This function can be used to:
488 * - test for resource conflicts with ACPI
489 * - request the resources
490 * - release the resources
491 * We only allocate the I/O ports we really need, to minimize the risk of
492 * conflicts with ACPI or with other drivers.
493 */
494static int smsc47m1_handle_resources(unsigned short address, enum chips type,
495 int action, struct device *dev)
496{
497 static const u8 ports_m1[] = {
498 /* register, region length */
499 0x04, 1,
500 0x33, 4,
501 0x56, 7,
502 };
503
504 static const u8 ports_m2[] = {
505 /* register, region length */
506 0x04, 1,
507 0x09, 1,
508 0x2c, 2,
509 0x35, 4,
510 0x56, 7,
511 0x69, 4,
512 };
513
514 int i, ports_size, err;
515 const u8 *ports;
516
517 switch (type) {
518 case smsc47m1:
519 default:
520 ports = ports_m1;
521 ports_size = ARRAY_SIZE(ports_m1);
522 break;
523 case smsc47m2:
524 ports = ports_m2;
525 ports_size = ARRAY_SIZE(ports_m2);
526 break;
527 }
528
529 for (i = 0; i + 1 < ports_size; i += 2) {
530 unsigned short start = address + ports[i];
531 unsigned short len = ports[i + 1];
532
533 switch (action) {
534 case CHECK:
535 /* Only check for conflicts */
536 err = acpi_check_region(start, len, DRVNAME);
537 if (err)
538 return err;
539 break;
540 case REQUEST:
541 /* Request the resources */
542 if (!request_region(start, len, DRVNAME)) {
543 dev_err(dev, "Region 0x%hx-0x%hx already in "
544 "use!\n", start, start + len);
545
546 /* Undo all requests */
547 for (i -= 2; i >= 0; i -= 2)
548 release_region(address + ports[i],
549 ports[i + 1]);
550 return -EBUSY;
551 }
552 break;
553 case RELEASE:
554 /* Release the resources */
555 release_region(start, len);
556 break;
557 }
558 }
559
560 return 0;
561}
562
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100563static int __init smsc47m1_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200565 struct device *dev = &pdev->dev;
566 struct smsc47m1_sio_data *sio_data = dev->platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 struct smsc47m1_data *data;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200568 struct resource *res;
Jean Delvarea0e92d72009-12-16 21:38:26 +0100569 int err;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200570 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Jean Delvare51f2cca2007-05-08 17:22:00 +0200572 static const char *names[] = {
573 "smsc47m1",
574 "smsc47m2",
575 };
576
577 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100578 err = smsc47m1_handle_resources(res->start, sio_data->type,
579 REQUEST, dev);
580 if (err < 0)
581 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200583 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 err = -ENOMEM;
585 goto error_release;
586 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Jean Delvare51f2cca2007-05-08 17:22:00 +0200588 data->addr = res->start;
589 data->type = sio_data->type;
590 data->name = names[sio_data->type];
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100591 mutex_init(&data->update_lock);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200592 platform_set_drvdata(pdev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
594 /* If no function is properly configured, there's no point in
595 actually registering the chip. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200596 pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 == 0x04;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200598 pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200600 if (data->type == smsc47m2) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200601 fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200602 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200603 fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200604 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200605 fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200606 & 0x0d) == 0x0d;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200607 pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200608 & 0x0d) == 0x08;
609 } else {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200610 fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200611 & 0x05) == 0x05;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200612 fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200613 & 0x05) == 0x05;
614 fan3 = 0;
615 pwm3 = 0;
616 }
617 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200618 dev_warn(dev, "Device not configured, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 err = -ENODEV;
620 goto error_free;
621 }
622
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 /* Some values (fan min, clock dividers, pwm registers) may be
624 needed before any update is triggered, so we better read them
625 at least once here. We don't usually do it that way, but in
626 this particular case, manually reading 5 registers out of 8
627 doesn't make much sense and we're better using the existing
628 function. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200629 smsc47m1_update_device(dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400631 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 if (fan1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200633 if ((err = device_create_file(dev,
634 &sensor_dev_attr_fan1_input.dev_attr))
635 || (err = device_create_file(dev,
636 &sensor_dev_attr_fan1_min.dev_attr))
637 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100638 &sensor_dev_attr_fan1_div.dev_attr))
639 || (err = device_create_file(dev,
640 &sensor_dev_attr_fan1_alarm.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200641 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200643 dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
645 if (fan2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200646 if ((err = device_create_file(dev,
647 &sensor_dev_attr_fan2_input.dev_attr))
648 || (err = device_create_file(dev,
649 &sensor_dev_attr_fan2_min.dev_attr))
650 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100651 &sensor_dev_attr_fan2_div.dev_attr))
652 || (err = device_create_file(dev,
653 &sensor_dev_attr_fan2_alarm.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200654 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200656 dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
Jean Delvare8eccbb62007-05-08 17:21:59 +0200658 if (fan3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200659 if ((err = device_create_file(dev,
660 &sensor_dev_attr_fan3_input.dev_attr))
661 || (err = device_create_file(dev,
662 &sensor_dev_attr_fan3_min.dev_attr))
663 || (err = device_create_file(dev,
Jean Delvare1f08af72008-01-06 15:36:13 +0100664 &sensor_dev_attr_fan3_div.dev_attr))
665 || (err = device_create_file(dev,
666 &sensor_dev_attr_fan3_alarm.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200667 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200668 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200669 dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200670
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 if (pwm1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200672 if ((err = device_create_file(dev,
673 &sensor_dev_attr_pwm1.dev_attr))
674 || (err = device_create_file(dev,
675 &sensor_dev_attr_pwm1_enable.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, "PWM 1 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 if (pwm2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200681 if ((err = device_create_file(dev,
682 &sensor_dev_attr_pwm2.dev_attr))
683 || (err = device_create_file(dev,
684 &sensor_dev_attr_pwm2_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200685 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200687 dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688
Jean Delvare8eccbb62007-05-08 17:21:59 +0200689 if (pwm3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200690 if ((err = device_create_file(dev,
691 &sensor_dev_attr_pwm3.dev_attr))
692 || (err = device_create_file(dev,
693 &sensor_dev_attr_pwm3_enable.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200694 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200695 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200696 dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200697
Jean Delvare51f2cca2007-05-08 17:22:00 +0200698 if ((err = device_create_file(dev, &dev_attr_alarms)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200699 goto error_remove_files;
Jean Delvare68a50b52007-08-12 13:58:50 +0200700 if ((err = device_create_file(dev, &dev_attr_name)))
701 goto error_remove_files;
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200702
Tony Jones1beeffe2007-08-20 13:46:20 -0700703 data->hwmon_dev = hwmon_device_register(dev);
704 if (IS_ERR(data->hwmon_dev)) {
705 err = PTR_ERR(data->hwmon_dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200706 goto error_remove_files;
707 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
709 return 0;
710
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200711error_remove_files:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200712 sysfs_remove_group(&dev->kobj, &smsc47m1_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713error_free:
Jean Delvare04a62172007-06-12 13:57:19 +0200714 platform_set_drvdata(pdev, NULL);
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400715 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716error_release:
Jean Delvarea0e92d72009-12-16 21:38:26 +0100717 smsc47m1_handle_resources(res->start, sio_data->type, RELEASE, dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 return err;
719}
720
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100721static int __exit smsc47m1_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200723 struct smsc47m1_data *data = platform_get_drvdata(pdev);
724 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
Tony Jones1beeffe2007-08-20 13:46:20 -0700726 hwmon_device_unregister(data->hwmon_dev);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200727 sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400728
Jean Delvare51f2cca2007-05-08 17:22:00 +0200729 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
Jean Delvarea0e92d72009-12-16 21:38:26 +0100730 smsc47m1_handle_resources(res->start, data->type, RELEASE, &pdev->dev);
Jean Delvare04a62172007-06-12 13:57:19 +0200731 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400732 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
734 return 0;
735}
736
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
738 int init)
739{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200740 struct smsc47m1_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100742 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
744 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200745 int i, fan_nr;
746 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747
Jean Delvare8eccbb62007-05-08 17:21:59 +0200748 for (i = 0; i < fan_nr; i++) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200749 data->fan[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200750 SMSC47M1_REG_FAN[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200751 data->fan_preload[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200752 SMSC47M1_REG_FAN_PRELOAD[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200753 data->pwm[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200754 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 }
756
Jean Delvare51f2cca2007-05-08 17:22:00 +0200757 i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 data->fan_div[0] = (i >> 4) & 0x03;
759 data->fan_div[1] = i >> 6;
760
Jean Delvare51f2cca2007-05-08 17:22:00 +0200761 data->alarms = smsc47m1_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 SMSC47M1_REG_ALARM) >> 6;
763 /* Clear alarms if needed */
764 if (data->alarms)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200765 smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766
Jean Delvare8eccbb62007-05-08 17:21:59 +0200767 if (fan_nr >= 3) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200768 data->fan_div[2] = (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200769 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200770 data->alarms |= (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200771 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
772 /* Clear alarm if needed */
773 if (data->alarms & 0x04)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200774 smsc47m1_write_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200775 SMSC47M2_REG_ALARM6,
776 0x40);
777 }
778
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 data->last_updated = jiffies;
780 }
781
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100782 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 return data;
784}
785
Jean Delvare51f2cca2007-05-08 17:22:00 +0200786static int __init smsc47m1_device_add(unsigned short address,
787 const struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200789 struct resource res = {
790 .start = address,
791 .end = address + SMSC_EXTENT - 1,
792 .name = DRVNAME,
793 .flags = IORESOURCE_IO,
794 };
795 int err;
796
Jean Delvarea0e92d72009-12-16 21:38:26 +0100797 err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
Jean Delvareb9acb642009-01-07 16:37:35 +0100798 if (err)
799 goto exit;
800
Jean Delvare51f2cca2007-05-08 17:22:00 +0200801 pdev = platform_device_alloc(DRVNAME, address);
802 if (!pdev) {
803 err = -ENOMEM;
804 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
805 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 }
807
Jean Delvare51f2cca2007-05-08 17:22:00 +0200808 err = platform_device_add_resources(pdev, &res, 1);
809 if (err) {
810 printk(KERN_ERR DRVNAME ": Device resource addition failed "
811 "(%d)\n", err);
812 goto exit_device_put;
813 }
814
Jean Delvare2df6d812007-06-09 10:11:16 -0400815 err = platform_device_add_data(pdev, sio_data,
816 sizeof(struct smsc47m1_sio_data));
817 if (err) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200818 printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
819 goto exit_device_put;
820 }
Jean Delvare51f2cca2007-05-08 17:22:00 +0200821
822 err = platform_device_add(pdev);
823 if (err) {
824 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
825 err);
826 goto exit_device_put;
827 }
828
829 return 0;
830
831exit_device_put:
832 platform_device_put(pdev);
833exit:
834 return err;
835}
836
837static int __init sm_smsc47m1_init(void)
838{
839 int err;
840 unsigned short address;
841 struct smsc47m1_sio_data sio_data;
842
843 if (smsc47m1_find(&address, &sio_data))
844 return -ENODEV;
845
Jean Delvare51f2cca2007-05-08 17:22:00 +0200846 /* Sets global pdev as a side effect */
847 err = smsc47m1_device_add(address, &sio_data);
848 if (err)
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100849 goto exit;
850
851 err = platform_driver_probe(&smsc47m1_driver, smsc47m1_probe);
852 if (err)
853 goto exit_device;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200854
855 return 0;
856
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100857exit_device:
858 platform_device_unregister(pdev);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200859exit:
860 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861}
862
863static void __exit sm_smsc47m1_exit(void)
864{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200865 platform_driver_unregister(&smsc47m1_driver);
Jean Delvare3ecf44b2009-12-16 21:38:26 +0100866 platform_device_unregister(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867}
868
869MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
870MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
871MODULE_LICENSE("GPL");
872
873module_init(sm_smsc47m1_init);
874module_exit(sm_smsc47m1_exit);