blob: 71792778d6f29452c5af56c0eff76dd97a5514ad [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <asm/io.h>
41
Jean Delvare51f2cca2007-05-08 17:22:00 +020042static struct platform_device *pdev;
43
44#define DRVNAME "smsc47m1"
Jean Delvare8eccbb62007-05-08 17:21:59 +020045enum chips { smsc47m1, smsc47m2 };
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47/* Super-I/0 registers and commands */
48
49#define REG 0x2e /* The register to read/write */
50#define VAL 0x2f /* The value to read/write */
51
52static inline void
53superio_outb(int reg, int val)
54{
55 outb(reg, REG);
56 outb(val, VAL);
57}
58
59static inline int
60superio_inb(int reg)
61{
62 outb(reg, REG);
63 return inb(VAL);
64}
65
66/* logical device for fans is 0x0A */
67#define superio_select() superio_outb(0x07, 0x0A)
68
69static inline void
70superio_enter(void)
71{
72 outb(0x55, REG);
73}
74
75static inline void
76superio_exit(void)
77{
78 outb(0xAA, REG);
79}
80
81#define SUPERIO_REG_ACT 0x30
82#define SUPERIO_REG_BASE 0x60
83#define SUPERIO_REG_DEVID 0x20
84
85/* Logical device registers */
86
87#define SMSC_EXTENT 0x80
88
89/* nr is 0 or 1 in the macros below */
90#define SMSC47M1_REG_ALARM 0x04
91#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr))
92#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr))
Linus Torvalds1da177e2005-04-16 15:20:36 -070093#define SMSC47M1_REG_FANDIV 0x58
Jean Delvare8eccbb62007-05-08 17:21:59 +020094
95static const u8 SMSC47M1_REG_FAN[3] = { 0x59, 0x5a, 0x6b };
96static const u8 SMSC47M1_REG_FAN_PRELOAD[3] = { 0x5b, 0x5c, 0x6c };
97static const u8 SMSC47M1_REG_PWM[3] = { 0x56, 0x57, 0x69 };
98
99#define SMSC47M2_REG_ALARM6 0x09
100#define SMSC47M2_REG_TPIN1 0x38
101#define SMSC47M2_REG_TPIN2 0x37
102#define SMSC47M2_REG_TPIN3 0x2d
103#define SMSC47M2_REG_PPIN3 0x2c
104#define SMSC47M2_REG_FANDIV3 0x6a
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105
106#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \
107 983040/((192-(reg))*(div)))
108#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \
109 983040/(((reg)-(preload))*(div)))
110#define DIV_FROM_REG(reg) (1 << (reg))
111#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1)
112#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01)
113#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E)
114
115struct smsc47m1_data {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200116 unsigned short addr;
117 const char *name;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200118 enum chips type;
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400119 struct class_device *class_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100121 struct mutex update_lock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 unsigned long last_updated; /* In jiffies */
123
Jean Delvare8eccbb62007-05-08 17:21:59 +0200124 u8 fan[3]; /* Register value */
125 u8 fan_preload[3]; /* Register value */
126 u8 fan_div[3]; /* Register encoding, shifted right */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 u8 alarms; /* Register encoding */
Jean Delvare8eccbb62007-05-08 17:21:59 +0200128 u8 pwm[3]; /* Register value (bit 0 is disable) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129};
130
Jean Delvare51f2cca2007-05-08 17:22:00 +0200131struct smsc47m1_sio_data {
132 enum chips type;
133};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134
Jean Delvare51f2cca2007-05-08 17:22:00 +0200135
136static int smsc47m1_probe(struct platform_device *pdev);
Jean Delvared0546122007-07-22 12:09:48 +0200137static int __devexit smsc47m1_remove(struct platform_device *pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
139 int init);
140
Jean Delvare51f2cca2007-05-08 17:22:00 +0200141static inline int smsc47m1_read_value(struct smsc47m1_data *data, u8 reg)
Jean Delvare94e183f2007-05-08 17:21:59 +0200142{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200143 return inb_p(data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200144}
145
Jean Delvare51f2cca2007-05-08 17:22:00 +0200146static inline void smsc47m1_write_value(struct smsc47m1_data *data, u8 reg,
Jean Delvare94e183f2007-05-08 17:21:59 +0200147 u8 value)
148{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200149 outb_p(value, data->addr + reg);
Jean Delvare94e183f2007-05-08 17:21:59 +0200150}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
Jean Delvare51f2cca2007-05-08 17:22:00 +0200152static struct platform_driver smsc47m1_driver = {
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100153 .driver = {
Jean Delvare87218842006-09-03 22:36:14 +0200154 .owner = THIS_MODULE,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200155 .name = DRVNAME,
Laurent Riffardcdaf7932005-11-26 20:37:41 +0100156 },
Jean Delvare51f2cca2007-05-08 17:22:00 +0200157 .probe = smsc47m1_probe,
158 .remove = __devexit_p(smsc47m1_remove),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159};
160
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200161static ssize_t get_fan(struct device *dev, struct device_attribute
162 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200164 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200166 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 /* This chip (stupidly) stops monitoring fan speed if PWM is
168 enabled and duty cycle is 0%. This is fine if the monitoring
169 and control concern the same fan, but troublesome if they are
170 not (which could as well happen). */
171 int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 :
172 FAN_FROM_REG(data->fan[nr],
173 DIV_FROM_REG(data->fan_div[nr]),
174 data->fan_preload[nr]);
175 return sprintf(buf, "%d\n", rpm);
176}
177
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200178static ssize_t get_fan_min(struct device *dev, struct device_attribute
179 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200181 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200183 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 int rpm = MIN_FROM_REG(data->fan_preload[nr],
185 DIV_FROM_REG(data->fan_div[nr]));
186 return sprintf(buf, "%d\n", rpm);
187}
188
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200189static ssize_t get_fan_div(struct device *dev, struct device_attribute
190 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200192 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200194 return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195}
196
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200197static ssize_t get_pwm(struct device *dev, struct device_attribute
198 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200200 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200202 return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203}
204
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200205static ssize_t get_pwm_en(struct device *dev, struct device_attribute
206 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200208 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200210 return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[attr->index]));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211}
212
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200213static ssize_t get_alarms(struct device *dev, struct device_attribute
214 *devattr, char *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215{
216 struct smsc47m1_data *data = smsc47m1_update_device(dev, 0);
217 return sprintf(buf, "%d\n", data->alarms);
218}
219
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200220static ssize_t set_fan_min(struct device *dev, struct device_attribute
221 *devattr, const char *buf, size_t count)
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);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200224 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200225 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 long rpmdiv, val = simple_strtol(buf, NULL, 10);
227
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100228 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]);
230
231 if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) {
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100232 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 return -EINVAL;
234 }
235
236 data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200237 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100239 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240
241 return count;
242}
243
244/* Note: we save and restore the fan minimum here, because its value is
245 determined in part by the fan clock divider. This follows the principle
Andreas Mohrd6e05ed2006-06-26 18:35:02 +0200246 of least surprise; the user doesn't expect the fan minimum to change just
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 because the divider changed. */
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200248static ssize_t set_fan_div(struct device *dev, struct device_attribute
249 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200251 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200252 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200253 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 long new_div = simple_strtol(buf, NULL, 10), tmp;
255 u8 old_div = DIV_FROM_REG(data->fan_div[nr]);
256
257 if (new_div == old_div) /* No change */
258 return count;
259
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100260 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 switch (new_div) {
262 case 1: data->fan_div[nr] = 0; break;
263 case 2: data->fan_div[nr] = 1; break;
264 case 4: data->fan_div[nr] = 2; break;
265 case 8: data->fan_div[nr] = 3; break;
266 default:
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100267 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 return -EINVAL;
269 }
270
Jean Delvare8eccbb62007-05-08 17:21:59 +0200271 switch (nr) {
272 case 0:
273 case 1:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200274 tmp = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200275 & ~(0x03 << (4 + 2 * nr));
276 tmp |= data->fan_div[nr] << (4 + 2 * nr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200277 smsc47m1_write_value(data, SMSC47M1_REG_FANDIV, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200278 break;
279 case 2:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200280 tmp = smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) & 0xCF;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200281 tmp |= data->fan_div[2] << 4;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200282 smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200283 break;
284 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
286 /* Preserve fan min */
287 tmp = 192 - (old_div * (192 - data->fan_preload[nr])
288 + new_div / 2) / new_div;
289 data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200290 smsc47m1_write_value(data, SMSC47M1_REG_FAN_PRELOAD[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 data->fan_preload[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100292 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
294 return count;
295}
296
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200297static ssize_t set_pwm(struct device *dev, struct device_attribute
298 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200300 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200301 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200302 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 long val = simple_strtol(buf, NULL, 10);
304
305 if (val < 0 || val > 255)
306 return -EINVAL;
307
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100308 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 data->pwm[nr] &= 0x81; /* Preserve additional bits */
310 data->pwm[nr] |= PWM_TO_REG(val);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200311 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100313 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
315 return count;
316}
317
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200318static ssize_t set_pwm_en(struct device *dev, struct device_attribute
319 *devattr, const char *buf, size_t count)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320{
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200321 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200322 struct smsc47m1_data *data = dev_get_drvdata(dev);
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200323 int nr = attr->index;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 long val = simple_strtol(buf, NULL, 10);
325
326 if (val != 0 && val != 1)
327 return -EINVAL;
328
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100329 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 data->pwm[nr] &= 0xFE; /* preserve the other bits */
331 data->pwm[nr] |= !val;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200332 smsc47m1_write_value(data, SMSC47M1_REG_PWM[nr],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 data->pwm[nr]);
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100334 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336 return count;
337}
338
339#define fan_present(offset) \
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200340static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan, \
341 NULL, offset - 1); \
342static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
343 get_fan_min, set_fan_min, offset - 1); \
344static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \
345 get_fan_div, set_fan_div, offset - 1); \
346static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \
347 get_pwm, set_pwm, offset - 1); \
348static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \
349 get_pwm_en, set_pwm_en, offset - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351fan_present(1);
352fan_present(2);
Jean Delvare8eccbb62007-05-08 17:21:59 +0200353fan_present(3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
355static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
356
Jean Delvare51f2cca2007-05-08 17:22:00 +0200357static ssize_t show_name(struct device *dev, struct device_attribute
358 *devattr, char *buf)
359{
360 struct smsc47m1_data *data = dev_get_drvdata(dev);
361
362 return sprintf(buf, "%s\n", data->name);
363}
364static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
365
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200366/* Almost all sysfs files may or may not be created depending on the chip
367 setup so we create them individually. It is still convenient to define a
368 group to remove them all at once. */
369static struct attribute *smsc47m1_attributes[] = {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200370 &sensor_dev_attr_fan1_input.dev_attr.attr,
371 &sensor_dev_attr_fan1_min.dev_attr.attr,
372 &sensor_dev_attr_fan1_div.dev_attr.attr,
373 &sensor_dev_attr_fan2_input.dev_attr.attr,
374 &sensor_dev_attr_fan2_min.dev_attr.attr,
375 &sensor_dev_attr_fan2_div.dev_attr.attr,
376 &sensor_dev_attr_fan3_input.dev_attr.attr,
377 &sensor_dev_attr_fan3_min.dev_attr.attr,
378 &sensor_dev_attr_fan3_div.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200379
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200380 &sensor_dev_attr_pwm1.dev_attr.attr,
381 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
382 &sensor_dev_attr_pwm2.dev_attr.attr,
383 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
384 &sensor_dev_attr_pwm3.dev_attr.attr,
385 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200386
387 &dev_attr_alarms.attr,
Jean Delvare51f2cca2007-05-08 17:22:00 +0200388 &dev_attr_name.attr,
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200389 NULL
390};
391
392static const struct attribute_group smsc47m1_group = {
393 .attrs = smsc47m1_attributes,
394};
395
Jean Delvare51f2cca2007-05-08 17:22:00 +0200396static int __init smsc47m1_find(unsigned short *addr,
397 struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398{
399 u8 val;
400
401 superio_enter();
Jean Delvare51f2cca2007-05-08 17:22:00 +0200402 val = superio_inb(SUPERIO_REG_DEVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
404 /*
Jean Delvare60917802006-10-08 22:00:44 +0200405 * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x
406 * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 * The LPC47M15x and LPC47M192 chips "with hardware monitoring block"
Jean Delvareec5ce552005-04-26 22:09:43 +0200408 * can do much more besides (device id 0x60).
Jean Delvareb890a072005-10-26 22:21:24 +0200409 * The LPC47M997 is undocumented, but seems to be compatible with
410 * the LPC47M192, and has the same device id.
Jean Delvare8eccbb62007-05-08 17:21:59 +0200411 * The LPC47M292 (device id 0x6B) is somewhat compatible, but it
412 * supports a 3rd fan, and the pin configuration registers are
413 * unfortunately different.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200415 switch (val) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200416 case 0x51:
Jean Delvare620100c2007-05-08 17:22:00 +0200417 pr_info(DRVNAME ": Found SMSC LPC47B27x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200418 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200419 break;
420 case 0x59:
Jean Delvare620100c2007-05-08 17:22:00 +0200421 pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200422 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200423 break;
424 case 0x5F:
Jean Delvare620100c2007-05-08 17:22:00 +0200425 pr_info(DRVNAME ": Found SMSC LPC47M14x\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200426 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200427 break;
428 case 0x60:
Jean Delvare620100c2007-05-08 17:22:00 +0200429 pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200430 sio_data->type = smsc47m1;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200431 break;
432 case 0x6B:
Jean Delvare620100c2007-05-08 17:22:00 +0200433 pr_info(DRVNAME ": Found SMSC LPC47M292\n");
Jean Delvare51f2cca2007-05-08 17:22:00 +0200434 sio_data->type = smsc47m2;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200435 break;
436 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 superio_exit();
438 return -ENODEV;
439 }
440
441 superio_select();
Jean Delvare2d8672c2005-07-19 23:56:35 +0200442 *addr = (superio_inb(SUPERIO_REG_BASE) << 8)
443 | superio_inb(SUPERIO_REG_BASE + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 val = superio_inb(SUPERIO_REG_ACT);
Jean Delvare2d8672c2005-07-19 23:56:35 +0200445 if (*addr == 0 || (val & 0x01) == 0) {
Jean Delvare620100c2007-05-08 17:22:00 +0200446 pr_info(DRVNAME ": Device is disabled, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 superio_exit();
448 return -ENODEV;
449 }
450
451 superio_exit();
452 return 0;
453}
454
Jean Delvare51f2cca2007-05-08 17:22:00 +0200455static int __devinit smsc47m1_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200457 struct device *dev = &pdev->dev;
458 struct smsc47m1_sio_data *sio_data = dev->platform_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 struct smsc47m1_data *data;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200460 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 int err = 0;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200462 int fan1, fan2, fan3, pwm1, pwm2, pwm3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463
Jean Delvare51f2cca2007-05-08 17:22:00 +0200464 static const char *names[] = {
465 "smsc47m1",
466 "smsc47m2",
467 };
468
469 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
470 if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) {
471 dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
472 (unsigned long)res->start,
473 (unsigned long)res->end);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 return -EBUSY;
475 }
476
Deepak Saxenaba9c2e82005-10-17 23:08:32 +0200477 if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 err = -ENOMEM;
479 goto error_release;
480 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
Jean Delvare51f2cca2007-05-08 17:22:00 +0200482 data->addr = res->start;
483 data->type = sio_data->type;
484 data->name = names[sio_data->type];
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100485 mutex_init(&data->update_lock);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200486 platform_set_drvdata(pdev, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
488 /* If no function is properly configured, there's no point in
489 actually registering the chip. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200490 pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 == 0x04;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200492 pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 == 0x04;
Jean Delvare8eccbb62007-05-08 17:21:59 +0200494 if (data->type == smsc47m2) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200495 fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200496 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200497 fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200498 & 0x0d) == 0x09;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200499 fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200500 & 0x0d) == 0x0d;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200501 pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3)
Jean Delvare8eccbb62007-05-08 17:21:59 +0200502 & 0x0d) == 0x08;
503 } else {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200504 fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200505 & 0x05) == 0x05;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200506 fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200507 & 0x05) == 0x05;
508 fan3 = 0;
509 pwm3 = 0;
510 }
511 if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200512 dev_warn(dev, "Device not configured, will not use\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 err = -ENODEV;
514 goto error_free;
515 }
516
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 /* Some values (fan min, clock dividers, pwm registers) may be
518 needed before any update is triggered, so we better read them
519 at least once here. We don't usually do it that way, but in
520 this particular case, manually reading 5 registers out of 8
521 doesn't make much sense and we're better using the existing
522 function. */
Jean Delvare51f2cca2007-05-08 17:22:00 +0200523 smsc47m1_update_device(dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400525 /* Register sysfs hooks */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 if (fan1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200527 if ((err = device_create_file(dev,
528 &sensor_dev_attr_fan1_input.dev_attr))
529 || (err = device_create_file(dev,
530 &sensor_dev_attr_fan1_min.dev_attr))
531 || (err = device_create_file(dev,
532 &sensor_dev_attr_fan1_div.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200533 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200535 dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
537 if (fan2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200538 if ((err = device_create_file(dev,
539 &sensor_dev_attr_fan2_input.dev_attr))
540 || (err = device_create_file(dev,
541 &sensor_dev_attr_fan2_min.dev_attr))
542 || (err = device_create_file(dev,
543 &sensor_dev_attr_fan2_div.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200544 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200546 dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Jean Delvare8eccbb62007-05-08 17:21:59 +0200548 if (fan3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200549 if ((err = device_create_file(dev,
550 &sensor_dev_attr_fan3_input.dev_attr))
551 || (err = device_create_file(dev,
552 &sensor_dev_attr_fan3_min.dev_attr))
553 || (err = device_create_file(dev,
554 &sensor_dev_attr_fan3_div.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200555 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200556 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200557 dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 if (pwm1) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200560 if ((err = device_create_file(dev,
561 &sensor_dev_attr_pwm1.dev_attr))
562 || (err = device_create_file(dev,
563 &sensor_dev_attr_pwm1_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200564 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200566 dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200567
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 if (pwm2) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200569 if ((err = device_create_file(dev,
570 &sensor_dev_attr_pwm2.dev_attr))
571 || (err = device_create_file(dev,
572 &sensor_dev_attr_pwm2_enable.dev_attr)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200573 goto error_remove_files;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 } else
Jean Delvare51f2cca2007-05-08 17:22:00 +0200575 dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Jean Delvare8eccbb62007-05-08 17:21:59 +0200577 if (pwm3) {
Jean Delvaree84cfbc2007-05-08 17:22:00 +0200578 if ((err = device_create_file(dev,
579 &sensor_dev_attr_pwm3.dev_attr))
580 || (err = device_create_file(dev,
581 &sensor_dev_attr_pwm3_enable.dev_attr)))
Jean Delvare8eccbb62007-05-08 17:21:59 +0200582 goto error_remove_files;
Jean Delvare8477d022007-08-16 14:33:37 +0200583 } else if (data->type == smsc47m2)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200584 dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n");
Jean Delvare8eccbb62007-05-08 17:21:59 +0200585
Jean Delvare51f2cca2007-05-08 17:22:00 +0200586 if ((err = device_create_file(dev, &dev_attr_alarms)))
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200587 goto error_remove_files;
Jean Delvare68a50b52007-08-12 13:58:50 +0200588 if ((err = device_create_file(dev, &dev_attr_name)))
589 goto error_remove_files;
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200590
Jean Delvare51f2cca2007-05-08 17:22:00 +0200591 data->class_dev = hwmon_device_register(dev);
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200592 if (IS_ERR(data->class_dev)) {
593 err = PTR_ERR(data->class_dev);
594 goto error_remove_files;
595 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
597 return 0;
598
Jean Delvarece8c6ce12006-09-24 21:25:12 +0200599error_remove_files:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200600 sysfs_remove_group(&dev->kobj, &smsc47m1_group);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601error_free:
Jean Delvare04a62172007-06-12 13:57:19 +0200602 platform_set_drvdata(pdev, NULL);
Alexey Dobriyan1f57ff82005-08-26 01:49:14 +0400603 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604error_release:
Jean Delvare51f2cca2007-05-08 17:22:00 +0200605 release_region(res->start, SMSC_EXTENT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 return err;
607}
608
Jean Delvare51f2cca2007-05-08 17:22:00 +0200609static int __devexit smsc47m1_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200611 struct smsc47m1_data *data = platform_get_drvdata(pdev);
612 struct resource *res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400614 hwmon_device_unregister(data->class_dev);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200615 sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400616
Jean Delvare51f2cca2007-05-08 17:22:00 +0200617 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
618 release_region(res->start, SMSC_EXTENT);
Jean Delvare04a62172007-06-12 13:57:19 +0200619 platform_set_drvdata(pdev, NULL);
Mark M. Hoffman943b0832005-07-15 21:39:18 -0400620 kfree(data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621
622 return 0;
623}
624
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
626 int init)
627{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200628 struct smsc47m1_data *data = dev_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100630 mutex_lock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631
632 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) {
Jean Delvare8eccbb62007-05-08 17:21:59 +0200633 int i, fan_nr;
634 fan_nr = data->type == smsc47m2 ? 3 : 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
Jean Delvare8eccbb62007-05-08 17:21:59 +0200636 for (i = 0; i < fan_nr; i++) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200637 data->fan[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200638 SMSC47M1_REG_FAN[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200639 data->fan_preload[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200640 SMSC47M1_REG_FAN_PRELOAD[i]);
Jean Delvare51f2cca2007-05-08 17:22:00 +0200641 data->pwm[i] = smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200642 SMSC47M1_REG_PWM[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643 }
644
Jean Delvare51f2cca2007-05-08 17:22:00 +0200645 i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 data->fan_div[0] = (i >> 4) & 0x03;
647 data->fan_div[1] = i >> 6;
648
Jean Delvare51f2cca2007-05-08 17:22:00 +0200649 data->alarms = smsc47m1_read_value(data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 SMSC47M1_REG_ALARM) >> 6;
651 /* Clear alarms if needed */
652 if (data->alarms)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200653 smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Jean Delvare8eccbb62007-05-08 17:21:59 +0200655 if (fan_nr >= 3) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200656 data->fan_div[2] = (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200657 SMSC47M2_REG_FANDIV3) >> 4) & 0x03;
Jean Delvare51f2cca2007-05-08 17:22:00 +0200658 data->alarms |= (smsc47m1_read_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200659 SMSC47M2_REG_ALARM6) & 0x40) >> 4;
660 /* Clear alarm if needed */
661 if (data->alarms & 0x04)
Jean Delvare51f2cca2007-05-08 17:22:00 +0200662 smsc47m1_write_value(data,
Jean Delvare8eccbb62007-05-08 17:21:59 +0200663 SMSC47M2_REG_ALARM6,
664 0x40);
665 }
666
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 data->last_updated = jiffies;
668 }
669
Ingo Molnar9a61bf62006-01-18 23:19:26 +0100670 mutex_unlock(&data->update_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 return data;
672}
673
Jean Delvare51f2cca2007-05-08 17:22:00 +0200674static int __init smsc47m1_device_add(unsigned short address,
675 const struct smsc47m1_sio_data *sio_data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200677 struct resource res = {
678 .start = address,
679 .end = address + SMSC_EXTENT - 1,
680 .name = DRVNAME,
681 .flags = IORESOURCE_IO,
682 };
683 int err;
684
685 pdev = platform_device_alloc(DRVNAME, address);
686 if (!pdev) {
687 err = -ENOMEM;
688 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
689 goto exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 }
691
Jean Delvare51f2cca2007-05-08 17:22:00 +0200692 err = platform_device_add_resources(pdev, &res, 1);
693 if (err) {
694 printk(KERN_ERR DRVNAME ": Device resource addition failed "
695 "(%d)\n", err);
696 goto exit_device_put;
697 }
698
Jean Delvare2df6d812007-06-09 10:11:16 -0400699 err = platform_device_add_data(pdev, sio_data,
700 sizeof(struct smsc47m1_sio_data));
701 if (err) {
Jean Delvare51f2cca2007-05-08 17:22:00 +0200702 printk(KERN_ERR DRVNAME ": Platform data allocation failed\n");
703 goto exit_device_put;
704 }
Jean Delvare51f2cca2007-05-08 17:22:00 +0200705
706 err = platform_device_add(pdev);
707 if (err) {
708 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
709 err);
710 goto exit_device_put;
711 }
712
713 return 0;
714
715exit_device_put:
716 platform_device_put(pdev);
717exit:
718 return err;
719}
720
721static int __init sm_smsc47m1_init(void)
722{
723 int err;
724 unsigned short address;
725 struct smsc47m1_sio_data sio_data;
726
727 if (smsc47m1_find(&address, &sio_data))
728 return -ENODEV;
729
730 err = platform_driver_register(&smsc47m1_driver);
731 if (err)
732 goto exit;
733
734 /* Sets global pdev as a side effect */
735 err = smsc47m1_device_add(address, &sio_data);
736 if (err)
737 goto exit_driver;
738
739 return 0;
740
741exit_driver:
742 platform_driver_unregister(&smsc47m1_driver);
743exit:
744 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745}
746
747static void __exit sm_smsc47m1_exit(void)
748{
Jean Delvare51f2cca2007-05-08 17:22:00 +0200749 platform_device_unregister(pdev);
750 platform_driver_unregister(&smsc47m1_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751}
752
753MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
754MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");
755MODULE_LICENSE("GPL");
756
757module_init(sm_smsc47m1_init);
758module_exit(sm_smsc47m1_exit);