smsc47m1.c 22.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/*
    smsc47m1.c - Part of lm_sensors, Linux kernel modules
                 for hardware monitoring

5
    Supports the SMSC LPC47B27x, LPC47M10x, LPC47M112, LPC47M13x,
6 7
    LPC47M14x, LPC47M15x, LPC47M192, LPC47M292 and LPC47M997
    Super-I/O chips.
L
Linus Torvalds 已提交
8 9

    Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
10
    Copyright (C) 2004-2007 Jean Delvare <khali@linux-fr.org>
L
Linus Torvalds 已提交
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
    Ported to Linux 2.6 by Gabriele Gorla <gorlik@yahoo.com>
                        and Jean Delvare

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/jiffies.h>
33
#include <linux/platform_device.h>
34
#include <linux/hwmon.h>
35
#include <linux/hwmon-sysfs.h>
36
#include <linux/err.h>
L
Linus Torvalds 已提交
37
#include <linux/init.h>
38
#include <linux/mutex.h>
39
#include <linux/sysfs.h>
L
Linus Torvalds 已提交
40 41
#include <asm/io.h>

42 43 44 45
static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");

46 47 48
static struct platform_device *pdev;

#define DRVNAME "smsc47m1"
49
enum chips { smsc47m1, smsc47m2 };
L
Linus Torvalds 已提交
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

/* Super-I/0 registers and commands */

#define	REG	0x2e	/* The register to read/write */
#define	VAL	0x2f	/* The value to read/write */

static inline void
superio_outb(int reg, int val)
{
	outb(reg, REG);
	outb(val, VAL);
}

static inline int
superio_inb(int reg)
{
	outb(reg, REG);
	return inb(VAL);
}

/* logical device for fans is 0x0A */
#define superio_select() superio_outb(0x07, 0x0A)

static inline void
superio_enter(void)
{
	outb(0x55, REG);
}

static inline void
superio_exit(void)
{
	outb(0xAA, REG);
}

#define SUPERIO_REG_ACT		0x30
#define SUPERIO_REG_BASE	0x60
#define SUPERIO_REG_DEVID	0x20

/* Logical device registers */

#define SMSC_EXTENT		0x80

/* nr is 0 or 1 in the macros below */
#define SMSC47M1_REG_ALARM		0x04
#define SMSC47M1_REG_TPIN(nr)		(0x34 - (nr))
#define SMSC47M1_REG_PPIN(nr)		(0x36 - (nr))
#define SMSC47M1_REG_FANDIV		0x58
98 99 100 101 102 103 104 105 106 107 108

static const u8 SMSC47M1_REG_FAN[3]		= { 0x59, 0x5a, 0x6b };
static const u8 SMSC47M1_REG_FAN_PRELOAD[3]	= { 0x5b, 0x5c, 0x6c };
static const u8 SMSC47M1_REG_PWM[3]		= { 0x56, 0x57, 0x69 };

#define SMSC47M2_REG_ALARM6		0x09
#define SMSC47M2_REG_TPIN1		0x38
#define SMSC47M2_REG_TPIN2		0x37
#define SMSC47M2_REG_TPIN3		0x2d
#define SMSC47M2_REG_PPIN3		0x2c
#define SMSC47M2_REG_FANDIV3		0x6a
L
Linus Torvalds 已提交
109 110 111 112 113 114 115 116 117 118 119

#define MIN_FROM_REG(reg,div)		((reg)>=192 ? 0 : \
					 983040/((192-(reg))*(div)))
#define FAN_FROM_REG(reg,div,preload)	((reg)<=(preload) || (reg)==255 ? 0 : \
					 983040/(((reg)-(preload))*(div)))
#define DIV_FROM_REG(reg)		(1 << (reg))
#define PWM_FROM_REG(reg)		(((reg) & 0x7E) << 1)
#define PWM_EN_FROM_REG(reg)		((~(reg)) & 0x01)
#define PWM_TO_REG(reg)			(((reg) >> 1) & 0x7E)

struct smsc47m1_data {
120 121
	unsigned short addr;
	const char *name;
122
	enum chips type;
123
	struct device *hwmon_dev;
L
Linus Torvalds 已提交
124

125
	struct mutex update_lock;
L
Linus Torvalds 已提交
126 127
	unsigned long last_updated;	/* In jiffies */

128 129 130
	u8 fan[3];		/* Register value */
	u8 fan_preload[3];	/* Register value */
	u8 fan_div[3];		/* Register encoding, shifted right */
L
Linus Torvalds 已提交
131
	u8 alarms;		/* Register encoding */
132
	u8 pwm[3];		/* Register value (bit 0 is disable) */
L
Linus Torvalds 已提交
133 134
};

135 136 137
struct smsc47m1_sio_data {
	enum chips type;
};
L
Linus Torvalds 已提交
138

139 140

static int smsc47m1_probe(struct platform_device *pdev);
141
static int __devexit smsc47m1_remove(struct platform_device *pdev);
L
Linus Torvalds 已提交
142 143 144
static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
		int init);

145
static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg)
146
{
147
	return inb_p(data->addr + reg);
148 149
}

150
static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg,
151 152
		u8 value)
{
153
	outb_p(value, data->addr + reg);
154
}
L
Linus Torvalds 已提交
155

156
static struct platform_driver smsc47m1_driver = {
157
	.driver = {
J
Jean Delvare 已提交
158
		.owner	= THIS_MODULE,
159
		.name	= DRVNAME,
160
	},
161 162
	.probe		= smsc47m1_probe,
	.remove		= __devexit_p(smsc47m1_remove),
L
Linus Torvalds 已提交
163 164
};

165 166
static ssize_t get_fan(struct device *dev, struct device_attribute
		       *devattr, char *buf)
L
Linus Torvalds 已提交
167
{
168
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
L
Linus Torvalds 已提交
169
	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
170
	int nr = attr->index;
L
Linus Torvalds 已提交
171 172 173 174 175 176 177 178 179 180 181
	/* This chip (stupidly) stops monitoring fan speed if PWM is
	   enabled and duty cycle is 0%. This is fine if the monitoring
	   and control concern the same fan, but troublesome if they are
	   not (which could as well happen). */
	int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
		  FAN_FROM_REG(data->fan[nr],
			       DIV_FROM_REG(data->fan_div[nr]),
			       data->fan_preload[nr]);
	return sprintf(buf, "%d\n", rpm);
}

182 183
static ssize_t get_fan_min(struct device *dev, struct device_attribute
			   *devattr, char *buf)
L
Linus Torvalds 已提交
184
{
185
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
L
Linus Torvalds 已提交
186
	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
187
	int nr = attr->index;
L
Linus Torvalds 已提交
188 189 190 191 192
	int rpm = MIN_FROM_REG(data->fan_preload[nr],
			       DIV_FROM_REG(data->fan_div[nr]));
	return sprintf(buf, "%d\n", rpm);
}

193 194
static ssize_t get_fan_div(struct device *dev, struct device_attribute
			   *devattr, char *buf)
L
Linus Torvalds 已提交
195
{
196
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
L
Linus Torvalds 已提交
197
	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
198
	return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
L
Linus Torvalds 已提交
199 200
}

201 202 203 204 205 206 207 208
static ssize_t get_fan_alarm(struct device *dev, struct device_attribute
			     *devattr, char *buf)
{
	int bitnr = to_sensor_dev_attr(devattr)->index;
	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
	return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
}

209 210
static ssize_t get_pwm(struct device *dev, struct device_attribute
		       *devattr, char *buf)
L
Linus Torvalds 已提交
211
{
212
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
L
Linus Torvalds 已提交
213
	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
214
	return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
L
Linus Torvalds 已提交
215 216
}

217 218
static ssize_t get_pwm_en(struct device *dev, struct device_attribute
			  *devattr, char *buf)
L
Linus Torvalds 已提交
219
{
220
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
L
Linus Torvalds 已提交
221
	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
222
	return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
L
Linus Torvalds 已提交
223 224
}

225 226
static ssize_t get_alarms(struct device *dev, struct device_attribute
			  *devattr, char *buf)
L
Linus Torvalds 已提交
227 228 229 230 231
{
	struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
	return sprintf(buf, "%d\n", data->alarms);
}

232 233
static ssize_t set_fan_min(struct device *dev, struct device_attribute
			   *devattr, const char *buf, size_t count)
L
Linus Torvalds 已提交
234
{
235
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
236
	struct smsc47m1_data *data = dev_get_drvdata(dev);
237
	int nr = attr->index;
L
Linus Torvalds 已提交
238 239
	long rpmdiv, val = simple_strtol(buf, NULL, 10);

240
	mutex_lock(&data->update_lock);
L
Linus Torvalds 已提交
241 242 243
	rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);

	if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
244
		mutex_unlock(&data->update_lock);
L
Linus Torvalds 已提交
245 246 247 248
		return -EINVAL;
	}

	data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
249
	smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
L
Linus Torvalds 已提交
250
			     data->fan_preload[nr]);
251
	mutex_unlock(&data->update_lock);
L
Linus Torvalds 已提交
252 253 254 255 256 257

	return count;
}

/* Note: we save and restore the fan minimum here, because its value is
   determined in part by the fan clock divider.  This follows the principle
A
Andreas Mohr 已提交
258
   of least surprise; the user doesn't expect the fan minimum to change just
L
Linus Torvalds 已提交
259
   because the divider changed. */
260 261
static ssize_t set_fan_div(struct device *dev, struct device_attribute
			   *devattr, const char *buf, size_t count)
L
Linus Torvalds 已提交
262
{
263
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
264
	struct smsc47m1_data *data = dev_get_drvdata(dev);
265
	int nr = attr->index;
L
Linus Torvalds 已提交
266 267 268 269 270 271
	long new_div = simple_strtol(buf, NULL, 10), tmp;
	u8 old_div = DIV_FROM_REG(data->fan_div[nr]);

	if (new_div == old_div) /* No change */
		return count;

272
	mutex_lock(&data->update_lock);
L
Linus Torvalds 已提交
273 274 275 276 277 278
	switch (new_div) {
	case 1: data->fan_div[nr] = 0; break;
	case 2: data->fan_div[nr] = 1; break;
	case 4: data->fan_div[nr] = 2; break;
	case 8: data->fan_div[nr] = 3; break;
	default:
279
		mutex_unlock(&data->update_lock);
L
Linus Torvalds 已提交
280 281 282
		return -EINVAL;
	}

283 284 285
	switch (nr) {
	case 0:
	case 1:
286
		tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
287 288
		      & ~(0x03 << (4 + 2 * nr));
		tmp |= data->fan_div[nr] << (4 + 2 * nr);
289
		smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
290 291
		break;
	case 2:
292
		tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
293
		tmp |= data->fan_div[2] << 4;
294
		smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
295 296
		break;
	}
L
Linus Torvalds 已提交
297 298 299 300 301

	/* Preserve fan min */
	tmp = 192 - (old_div * (192 - data->fan_preload[nr])
		     + new_div / 2) / new_div;
	data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
302
	smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
L
Linus Torvalds 已提交
303
			     data->fan_preload[nr]);
304
	mutex_unlock(&data->update_lock);
L
Linus Torvalds 已提交
305 306 307 308

	return count;
}

309 310
static ssize_t set_pwm(struct device *dev, struct device_attribute
		       *devattr, const char *buf, size_t count)
L
Linus Torvalds 已提交
311
{
312
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
313
	struct smsc47m1_data *data = dev_get_drvdata(dev);
314
	int nr = attr->index;
L
Linus Torvalds 已提交
315 316 317 318 319
	long val = simple_strtol(buf, NULL, 10);

	if (val < 0 || val > 255)
		return -EINVAL;

320
	mutex_lock(&data->update_lock);
L
Linus Torvalds 已提交
321 322
	data->pwm[nr] &= 0x81; /* Preserve additional bits */
	data->pwm[nr] |= PWM_TO_REG(val);
323
	smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
L
Linus Torvalds 已提交
324
			     data->pwm[nr]);
325
	mutex_unlock(&data->update_lock);
L
Linus Torvalds 已提交
326 327 328 329

	return count;
}

330 331
static ssize_t set_pwm_en(struct device *dev, struct device_attribute
			  *devattr, const char *buf, size_t count)
L
Linus Torvalds 已提交
332
{
333
	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
334
	struct smsc47m1_data *data = dev_get_drvdata(dev);
335
	int nr = attr->index;
L
Linus Torvalds 已提交
336 337 338 339 340
	long val = simple_strtol(buf, NULL, 10);
	
	if (val != 0 && val != 1)
		return -EINVAL;

341
	mutex_lock(&data->update_lock);
L
Linus Torvalds 已提交
342 343
	data->pwm[nr] &= 0xFE; /* preserve the other bits */
	data->pwm[nr] |= !val;
344
	smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
L
Linus Torvalds 已提交
345
			     data->pwm[nr]);
346
	mutex_unlock(&data->update_lock);
L
Linus Torvalds 已提交
347 348 349 350 351

	return count;
}

#define fan_present(offset)						\
352 353 354 355 356 357
static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan,	\
		NULL, offset - 1);					\
static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR,		\
		get_fan_min, set_fan_min, offset - 1);			\
static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR,		\
		get_fan_div, set_fan_div, offset - 1);			\
358 359
static SENSOR_DEVICE_ATTR(fan##offset##_alarm, S_IRUGO, get_fan_alarm,	\
		NULL, offset - 1);					\
360 361 362 363
static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR,		\
		get_pwm, set_pwm, offset - 1);				\
static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR,	\
		get_pwm_en, set_pwm_en, offset - 1)
L
Linus Torvalds 已提交
364 365 366

fan_present(1);
fan_present(2);
367
fan_present(3);
L
Linus Torvalds 已提交
368 369 370

static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);

371 372 373 374 375 376 377 378 379
static ssize_t show_name(struct device *dev, struct device_attribute
			 *devattr, char *buf)
{
	struct smsc47m1_data *data = dev_get_drvdata(dev);

	return sprintf(buf, "%s\n", data->name);
}
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);

380 381 382 383
/* Almost all sysfs files may or may not be created depending on the chip
   setup so we create them individually. It is still convenient to define a
   group to remove them all at once. */
static struct attribute *smsc47m1_attributes[] = {
384 385 386
	&sensor_dev_attr_fan1_input.dev_attr.attr,
	&sensor_dev_attr_fan1_min.dev_attr.attr,
	&sensor_dev_attr_fan1_div.dev_attr.attr,
387
	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
388 389 390
	&sensor_dev_attr_fan2_input.dev_attr.attr,
	&sensor_dev_attr_fan2_min.dev_attr.attr,
	&sensor_dev_attr_fan2_div.dev_attr.attr,
391
	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
392 393 394
	&sensor_dev_attr_fan3_input.dev_attr.attr,
	&sensor_dev_attr_fan3_min.dev_attr.attr,
	&sensor_dev_attr_fan3_div.dev_attr.attr,
395
	&sensor_dev_attr_fan3_alarm.dev_attr.attr,
396 397 398 399 400 401 402

	&sensor_dev_attr_pwm1.dev_attr.attr,
	&sensor_dev_attr_pwm1_enable.dev_attr.attr,
	&sensor_dev_attr_pwm2.dev_attr.attr,
	&sensor_dev_attr_pwm2_enable.dev_attr.attr,
	&sensor_dev_attr_pwm3.dev_attr.attr,
	&sensor_dev_attr_pwm3_enable.dev_attr.attr,
403 404

	&dev_attr_alarms.attr,
405
	&dev_attr_name.attr,
406 407 408 409 410 411 412
	NULL
};

static const struct attribute_group smsc47m1_group = {
	.attrs = smsc47m1_attributes,
};

413 414
static int __init smsc47m1_find(unsigned short *addr,
				struct smsc47m1_sio_data *sio_data)
L
Linus Torvalds 已提交
415 416 417 418
{
	u8 val;

	superio_enter();
419
	val = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID);
L
Linus Torvalds 已提交
420 421

	/*
422 423
	 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
	 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
L
Linus Torvalds 已提交
424
	 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
425
	 * can do much more besides (device id 0x60).
426 427
	 * The LPC47M997 is undocumented, but seems to be compatible with
	 * the LPC47M192, and has the same device id.
428 429 430
	 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
	 * supports a 3rd fan, and the pin configuration registers are
	 * unfortunately different.
L
Linus Torvalds 已提交
431
	 */
432
	switch (val) {
433
	case 0x51:
434
		pr_info(DRVNAME ": Found SMSC LPC47B27x\n");
435
		sio_data->type = smsc47m1;
436 437
		break;
	case 0x59:
438
		pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
439
		sio_data->type = smsc47m1;
440 441
		break;
	case 0x5F:
442
		pr_info(DRVNAME ": Found SMSC LPC47M14x\n");
443
		sio_data->type = smsc47m1;
444 445
		break;
	case 0x60:
446
		pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
447
		sio_data->type = smsc47m1;
448 449
		break;
	case 0x6B:
450
		pr_info(DRVNAME ": Found SMSC LPC47M292\n");
451
		sio_data->type = smsc47m2;
452 453
		break;
	default:
L
Linus Torvalds 已提交
454 455 456 457 458
		superio_exit();
		return -ENODEV;
	}

	superio_select();
459 460
	*addr = (superio_inb(SUPERIO_REG_BASE) << 8)
	      |  superio_inb(SUPERIO_REG_BASE + 1);
L
Linus Torvalds 已提交
461
	val = superio_inb(SUPERIO_REG_ACT);
462
	if (*addr == 0 || (val & 0x01) == 0) {
463
		pr_info(DRVNAME ": Device is disabled, will not use\n");
L
Linus Torvalds 已提交
464 465 466 467 468 469 470 471
		superio_exit();
		return -ENODEV;
	}

	superio_exit();
	return 0;
}

472
static int __devinit smsc47m1_probe(struct platform_device *pdev)
L
Linus Torvalds 已提交
473
{
474 475
	struct device *dev = &pdev->dev;
	struct smsc47m1_sio_data *sio_data = dev->platform_data;
L
Linus Torvalds 已提交
476
	struct smsc47m1_data *data;
477
	struct resource *res;
L
Linus Torvalds 已提交
478
	int err = 0;
479
	int fan1, fan2, fan3, pwm1, pwm2, pwm3;
L
Linus Torvalds 已提交
480

481 482 483 484 485 486 487 488 489 490
	static const char *names[] = {
		"smsc47m1",
		"smsc47m2",
	};

	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
	if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) {
		dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
			(unsigned long)res->start,
			(unsigned long)res->end);
L
Linus Torvalds 已提交
491 492 493
		return -EBUSY;
	}

D
Deepak Saxena 已提交
494
	if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
L
Linus Torvalds 已提交
495 496 497 498
		err = -ENOMEM;
		goto error_release;
	}

499 500 501
	data->addr = res->start;
	data->type = sio_data->type;
	data->name = names[sio_data->type];
502
	mutex_init(&data->update_lock);
503
	platform_set_drvdata(pdev, data);
L
Linus Torvalds 已提交
504 505 506

	/* If no function is properly configured, there's no point in
	   actually registering the chip. */
507
	pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
L
Linus Torvalds 已提交
508
	       == 0x04;
509
	pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
L
Linus Torvalds 已提交
510
	       == 0x04;
511
	if (data->type == smsc47m2) {
512
		fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
513
			& 0x0d) == 0x09;
514
		fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
515
			& 0x0d) == 0x09;
516
		fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
517
			& 0x0d) == 0x0d;
518
		pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
519 520
			& 0x0d) == 0x08;
	} else {
521
		fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
522
			& 0x05) == 0x05;
523
		fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
524 525 526 527 528
			& 0x05) == 0x05;
		fan3 = 0;
		pwm3 = 0;
	}
	if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
529
		dev_warn(dev, "Device not configured, will not use\n");
L
Linus Torvalds 已提交
530 531 532 533 534 535 536 537 538 539
		err = -ENODEV;
		goto error_free;
	}

	/* Some values (fan min, clock dividers, pwm registers) may be
	   needed before any update is triggered, so we better read them
	   at least once here. We don't usually do it that way, but in
	   this particular case, manually reading 5 registers out of 8
	   doesn't make much sense and we're better using the existing
	   function. */
540
	smsc47m1_update_device(dev, 1);
L
Linus Torvalds 已提交
541

542
	/* Register sysfs hooks */
L
Linus Torvalds 已提交
543
	if (fan1) {
544 545 546 547 548
		if ((err = device_create_file(dev,
				&sensor_dev_attr_fan1_input.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_fan1_min.dev_attr))
		 || (err = device_create_file(dev,
549 550 551
				&sensor_dev_attr_fan1_div.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_fan1_alarm.dev_attr)))
552
			goto error_remove_files;
L
Linus Torvalds 已提交
553
	} else
554
		dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
L
Linus Torvalds 已提交
555 556

	if (fan2) {
557 558 559 560 561
		if ((err = device_create_file(dev,
				&sensor_dev_attr_fan2_input.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_fan2_min.dev_attr))
		 || (err = device_create_file(dev,
562 563 564
				&sensor_dev_attr_fan2_div.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_fan2_alarm.dev_attr)))
565
			goto error_remove_files;
L
Linus Torvalds 已提交
566
	} else
567
		dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
L
Linus Torvalds 已提交
568

569
	if (fan3) {
570 571 572 573 574
		if ((err = device_create_file(dev,
				&sensor_dev_attr_fan3_input.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_fan3_min.dev_attr))
		 || (err = device_create_file(dev,
575 576 577
				&sensor_dev_attr_fan3_div.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_fan3_alarm.dev_attr)))
578
			goto error_remove_files;
579
	} else if (data->type == smsc47m2)
580
		dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
581

L
Linus Torvalds 已提交
582
	if (pwm1) {
583 584 585 586
		if ((err = device_create_file(dev,
				&sensor_dev_attr_pwm1.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_pwm1_enable.dev_attr)))
587
			goto error_remove_files;
L
Linus Torvalds 已提交
588
	} else
589
		dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
590

L
Linus Torvalds 已提交
591
	if (pwm2) {
592 593 594 595
		if ((err = device_create_file(dev,
				&sensor_dev_attr_pwm2.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_pwm2_enable.dev_attr)))
596
			goto error_remove_files;
L
Linus Torvalds 已提交
597
	} else
598
		dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
L
Linus Torvalds 已提交
599

600
	if (pwm3) {
601 602 603 604
		if ((err = device_create_file(dev,
				&sensor_dev_attr_pwm3.dev_attr))
		 || (err = device_create_file(dev,
				&sensor_dev_attr_pwm3_enable.dev_attr)))
605
			goto error_remove_files;
606
	} else if (data->type == smsc47m2)
607
		dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
608

609
	if ((err = device_create_file(dev, &dev_attr_alarms)))
610
		goto error_remove_files;
611 612
	if ((err = device_create_file(dev, &dev_attr_name)))
		goto error_remove_files;
613

614 615 616
	data->hwmon_dev = hwmon_device_register(dev);
	if (IS_ERR(data->hwmon_dev)) {
		err = PTR_ERR(data->hwmon_dev);
617 618
		goto error_remove_files;
	}
L
Linus Torvalds 已提交
619 620 621

	return 0;

622
error_remove_files:
623
	sysfs_remove_group(&dev->kobj, &smsc47m1_group);
L
Linus Torvalds 已提交
624
error_free:
625
	platform_set_drvdata(pdev, NULL);
626
	kfree(data);
L
Linus Torvalds 已提交
627
error_release:
628
	release_region(res->start, SMSC_EXTENT);
L
Linus Torvalds 已提交
629 630 631
	return err;
}

632
static int __devexit smsc47m1_remove(struct platform_device *pdev)
L
Linus Torvalds 已提交
633
{
634 635
	struct smsc47m1_data *data = platform_get_drvdata(pdev);
	struct resource *res;
L
Linus Torvalds 已提交
636

637
	hwmon_device_unregister(data->hwmon_dev);
638
	sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
L
Linus Torvalds 已提交
639

640 641
	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
	release_region(res->start, SMSC_EXTENT);
642
	platform_set_drvdata(pdev, NULL);
643
	kfree(data);
L
Linus Torvalds 已提交
644 645 646 647 648 649 650

	return 0;
}

static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
		int init)
{
651
	struct smsc47m1_data *data = dev_get_drvdata(dev);
L
Linus Torvalds 已提交
652

653
	mutex_lock(&data->update_lock);
L
Linus Torvalds 已提交
654 655

	if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
656 657
		int i, fan_nr;
		fan_nr = data->type == smsc47m2 ? 3 : 2;
L
Linus Torvalds 已提交
658

659
		for (i = 0; i < fan_nr; i++) {
660
			data->fan[i] = smsc47m1_read_value(data,
661
				       SMSC47M1_REG_FAN[i]);
662
			data->fan_preload[i] = smsc47m1_read_value(data,
663
					       SMSC47M1_REG_FAN_PRELOAD[i]);
664
			data->pwm[i] = smsc47m1_read_value(data,
665
				       SMSC47M1_REG_PWM[i]);
L
Linus Torvalds 已提交
666 667
		}

668
		i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
L
Linus Torvalds 已提交
669 670 671
		data->fan_div[0] = (i >> 4) & 0x03;
		data->fan_div[1] = i >> 6;

672
		data->alarms = smsc47m1_read_value(data,
L
Linus Torvalds 已提交
673 674 675
			       SMSC47M1_REG_ALARM) >> 6;
		/* Clear alarms if needed */
		if (data->alarms)
676
			smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
L
Linus Torvalds 已提交
677

678
		if (fan_nr >= 3) {
679
			data->fan_div[2] = (smsc47m1_read_value(data,
680
					    SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
681
			data->alarms |= (smsc47m1_read_value(data,
682 683 684
					 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
			/* Clear alarm if needed */
			if (data->alarms & 0x04)
685
				smsc47m1_write_value(data,
686 687 688 689
						     SMSC47M2_REG_ALARM6,
						     0x40);
		}

L
Linus Torvalds 已提交
690 691 692
		data->last_updated = jiffies;
	}

693
	mutex_unlock(&data->update_lock);
L
Linus Torvalds 已提交
694 695 696
	return data;
}

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
static int __init smsc47m1_device_add(unsigned short address,
				      const struct smsc47m1_sio_data *sio_data)
{
	struct resource res = {
		.start	= address,
		.end	= address + SMSC_EXTENT - 1,
		.name	= DRVNAME,
		.flags	= IORESOURCE_IO,
	};
	int err;

	pdev = platform_device_alloc(DRVNAME, address);
	if (!pdev) {
		err = -ENOMEM;
		printk(KERN_ERR DRVNAME ": Device allocation failed\n");
		goto exit;
	}

	err = platform_device_add_resources(pdev, &res, 1);
	if (err) {
		printk(KERN_ERR DRVNAME ": Device resource addition failed "
		       "(%d)\n", err);
		goto exit_device_put;
	}

722 723 724
	err = platform_device_add_data(pdev, sio_data,
				       sizeof(struct smsc47m1_sio_data));
	if (err) {
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
		printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
		goto exit_device_put;
	}

	err = platform_device_add(pdev);
	if (err) {
		printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
		       err);
		goto exit_device_put;
	}

	return 0;

exit_device_put:
	platform_device_put(pdev);
exit:
	return err;
}

L
Linus Torvalds 已提交
744 745
static int __init sm_smsc47m1_init(void)
{
746 747 748 749 750
	int err;
	unsigned short address;
	struct smsc47m1_sio_data sio_data;

	if (smsc47m1_find(&address, &sio_data))
L
Linus Torvalds 已提交
751 752
		return -ENODEV;

753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
	err = platform_driver_register(&smsc47m1_driver);
	if (err)
		goto exit;

	/* Sets global pdev as a side effect */
	err = smsc47m1_device_add(address, &sio_data);
	if (err)
		goto exit_driver;

	return 0;

exit_driver:
	platform_driver_unregister(&smsc47m1_driver);
exit:
	return err;
L
Linus Torvalds 已提交
768 769 770 771
}

static void __exit sm_smsc47m1_exit(void)
{
772 773
	platform_device_unregister(pdev);
	platform_driver_unregister(&smsc47m1_driver);
L
Linus Torvalds 已提交
774 775 776 777 778 779 780 781
}

MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
MODULE_LICENSE("GPL");

module_init(sm_smsc47m1_init);
module_exit(sm_smsc47m1_exit);