aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hwmon/Kconfig3
-rw-r--r--drivers/hwmon/smsc47b397.c164
2 files changed, 109 insertions, 58 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 065851a77ccb..5216da02b8ba 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -477,8 +477,7 @@ config SENSORS_SMSC47M192
477 477
478config SENSORS_SMSC47B397 478config SENSORS_SMSC47B397
479 tristate "SMSC LPC47B397-NC" 479 tristate "SMSC LPC47B397-NC"
480 depends on HWMON && I2C && EXPERIMENTAL 480 depends on HWMON && EXPERIMENTAL
481 select I2C_ISA
482 help 481 help
483 If you say yes here you get support for the SMSC LPC47B397-NC 482 If you say yes here you get support for the SMSC LPC47B397-NC
484 sensor chip. 483 sensor chip.
diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c
index 72b0e2d8650c..09df56118e18 100644
--- a/drivers/hwmon/smsc47b397.c
+++ b/drivers/hwmon/smsc47b397.c
@@ -30,16 +30,16 @@
30#include <linux/slab.h> 30#include <linux/slab.h>
31#include <linux/ioport.h> 31#include <linux/ioport.h>
32#include <linux/jiffies.h> 32#include <linux/jiffies.h>
33#include <linux/i2c.h> 33#include <linux/platform_device.h>
34#include <linux/i2c-isa.h>
35#include <linux/hwmon.h> 34#include <linux/hwmon.h>
36#include <linux/err.h> 35#include <linux/err.h>
37#include <linux/init.h> 36#include <linux/init.h>
38#include <linux/mutex.h> 37#include <linux/mutex.h>
39#include <asm/io.h> 38#include <asm/io.h>
40 39
41/* Address is autodetected, there is no default value */ 40static struct platform_device *pdev;
42static unsigned short address; 41
42#define DRVNAME "smsc47b397"
43 43
44/* Super-I/0 registers and commands */ 44/* Super-I/0 registers and commands */
45 45
@@ -91,7 +91,8 @@ static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80};
91#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr)) 91#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr))
92 92
93struct smsc47b397_data { 93struct smsc47b397_data {
94 struct i2c_client client; 94 unsigned short addr;
95 const char *name;
95 struct class_device *class_dev; 96 struct class_device *class_dev;
96 struct mutex lock; 97 struct mutex lock;
97 98
@@ -104,45 +105,43 @@ struct smsc47b397_data {
104 u8 temp[4]; 105 u8 temp[4];
105}; 106};
106 107
107static int smsc47b397_read_value(struct i2c_client *client, u8 reg) 108static int smsc47b397_read_value(struct smsc47b397_data* data, u8 reg)
108{ 109{
109 struct smsc47b397_data *data = i2c_get_clientdata(client);
110 int res; 110 int res;
111 111
112 mutex_lock(&data->lock); 112 mutex_lock(&data->lock);
113 outb(reg, client->addr); 113 outb(reg, data->addr);
114 res = inb_p(client->addr + 1); 114 res = inb_p(data->addr + 1);
115 mutex_unlock(&data->lock); 115 mutex_unlock(&data->lock);
116 return res; 116 return res;
117} 117}
118 118
119static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) 119static struct smsc47b397_data *smsc47b397_update_device(struct device *dev)
120{ 120{
121 struct i2c_client *client = to_i2c_client(dev); 121 struct smsc47b397_data *data = dev_get_drvdata(dev);
122 struct smsc47b397_data *data = i2c_get_clientdata(client);
123 int i; 122 int i;
124 123
125 mutex_lock(&data->update_lock); 124 mutex_lock(&data->update_lock);
126 125
127 if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { 126 if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
128 dev_dbg(&client->dev, "starting device update...\n"); 127 dev_dbg(dev, "starting device update...\n");
129 128
130 /* 4 temperature inputs, 4 fan inputs */ 129 /* 4 temperature inputs, 4 fan inputs */
131 for (i = 0; i < 4; i++) { 130 for (i = 0; i < 4; i++) {
132 data->temp[i] = smsc47b397_read_value(client, 131 data->temp[i] = smsc47b397_read_value(data,
133 SMSC47B397_REG_TEMP(i)); 132 SMSC47B397_REG_TEMP(i));
134 133
135 /* must read LSB first */ 134 /* must read LSB first */
136 data->fan[i] = smsc47b397_read_value(client, 135 data->fan[i] = smsc47b397_read_value(data,
137 SMSC47B397_REG_FAN_LSB(i)); 136 SMSC47B397_REG_FAN_LSB(i));
138 data->fan[i] |= smsc47b397_read_value(client, 137 data->fan[i] |= smsc47b397_read_value(data,
139 SMSC47B397_REG_FAN_MSB(i)) << 8; 138 SMSC47B397_REG_FAN_MSB(i)) << 8;
140 } 139 }
141 140
142 data->last_updated = jiffies; 141 data->last_updated = jiffies;
143 data->valid = 1; 142 data->valid = 1;
144 143
145 dev_dbg(&client->dev, "... device update complete\n"); 144 dev_dbg(dev, "... device update complete\n");
146 } 145 }
147 146
148 mutex_unlock(&data->update_lock); 147 mutex_unlock(&data->update_lock);
@@ -202,6 +201,14 @@ sysfs_fan(2);
202sysfs_fan(3); 201sysfs_fan(3);
203sysfs_fan(4); 202sysfs_fan(4);
204 203
204static ssize_t show_name(struct device *dev, struct device_attribute
205 *devattr, char *buf)
206{
207 struct smsc47b397_data *data = dev_get_drvdata(dev);
208 return sprintf(buf, "%s\n", data->name);
209}
210static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
211
205static struct attribute *smsc47b397_attributes[] = { 212static struct attribute *smsc47b397_attributes[] = {
206 &dev_attr_temp1_input.attr, 213 &dev_attr_temp1_input.attr,
207 &dev_attr_temp2_input.attr, 214 &dev_attr_temp2_input.attr,
@@ -212,6 +219,7 @@ static struct attribute *smsc47b397_attributes[] = {
212 &dev_attr_fan3_input.attr, 219 &dev_attr_fan3_input.attr,
213 &dev_attr_fan4_input.attr, 220 &dev_attr_fan4_input.attr,
214 221
222 &dev_attr_name.attr,
215 NULL 223 NULL
216}; 224};
217 225
@@ -219,44 +227,44 @@ static const struct attribute_group smsc47b397_group = {
219 .attrs = smsc47b397_attributes, 227 .attrs = smsc47b397_attributes,
220}; 228};
221 229
222static int smsc47b397_detach_client(struct i2c_client *client) 230static int __devexit smsc47b397_remove(struct platform_device *pdev)
223{ 231{
224 struct smsc47b397_data *data = i2c_get_clientdata(client); 232 struct smsc47b397_data *data = platform_get_drvdata(pdev);
225 int err; 233 struct resource *res;
226 234
227 hwmon_device_unregister(data->class_dev); 235 hwmon_device_unregister(data->class_dev);
228 sysfs_remove_group(&client->dev.kobj, &smsc47b397_group); 236 sysfs_remove_group(&pdev->dev.kobj, &smsc47b397_group);
229 237 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
230 if ((err = i2c_detach_client(client))) 238 release_region(res->start, SMSC_EXTENT);
231 return err;
232
233 release_region(client->addr, SMSC_EXTENT);
234 kfree(data); 239 kfree(data);
235 240
236 return 0; 241 return 0;
237} 242}
238 243
239static int smsc47b397_detect(struct i2c_adapter *adapter); 244static int smsc47b397_probe(struct platform_device *pdev);
240 245
241static struct i2c_driver smsc47b397_driver = { 246static struct platform_driver smsc47b397_driver = {
242 .driver = { 247 .driver = {
243 .owner = THIS_MODULE, 248 .owner = THIS_MODULE,
244 .name = "smsc47b397", 249 .name = DRVNAME,
245 }, 250 },
246 .attach_adapter = smsc47b397_detect, 251 .probe = smsc47b397_probe,
247 .detach_client = smsc47b397_detach_client, 252 .remove = __devexit_p(smsc47b397_remove),
248}; 253};
249 254
250static int smsc47b397_detect(struct i2c_adapter *adapter) 255static int __devinit smsc47b397_probe(struct platform_device *pdev)
251{ 256{
252 struct i2c_client *new_client; 257 struct device *dev = &pdev->dev;
253 struct smsc47b397_data *data; 258 struct smsc47b397_data *data;
259 struct resource *res;
254 int err = 0; 260 int err = 0;
255 261
256 if (!request_region(address, SMSC_EXTENT, 262 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
263 if (!request_region(res->start, SMSC_EXTENT,
257 smsc47b397_driver.driver.name)) { 264 smsc47b397_driver.driver.name)) {
258 dev_err(&adapter->dev, "Region 0x%x already in use!\n", 265 dev_err(dev, "Region 0x%lx-0x%lx already in use!\n",
259 address); 266 (unsigned long)res->start,
267 (unsigned long)res->start + SMSC_EXTENT - 1);
260 return -EBUSY; 268 return -EBUSY;
261 } 269 }
262 270
@@ -265,25 +273,16 @@ static int smsc47b397_detect(struct i2c_adapter *adapter)
265 goto error_release; 273 goto error_release;
266 } 274 }
267 275
268 new_client = &data->client; 276 data->addr = res->start;
269 i2c_set_clientdata(new_client, data); 277 data->name = "smsc47b397";
270 new_client->addr = address;
271 mutex_init(&data->lock); 278 mutex_init(&data->lock);
272 new_client->adapter = adapter;
273 new_client->driver = &smsc47b397_driver;
274 new_client->flags = 0;
275
276 strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE);
277
278 mutex_init(&data->update_lock); 279 mutex_init(&data->update_lock);
280 platform_set_drvdata(pdev, data);
279 281
280 if ((err = i2c_attach_client(new_client))) 282 if ((err = sysfs_create_group(&dev->kobj, &smsc47b397_group)))
281 goto error_free; 283 goto error_free;
282 284
283 if ((err = sysfs_create_group(&new_client->dev.kobj, &smsc47b397_group))) 285 data->class_dev = hwmon_device_register(dev);
284 goto error_detach;
285
286 data->class_dev = hwmon_device_register(&new_client->dev);
287 if (IS_ERR(data->class_dev)) { 286 if (IS_ERR(data->class_dev)) {
288 err = PTR_ERR(data->class_dev); 287 err = PTR_ERR(data->class_dev);
289 goto error_remove; 288 goto error_remove;
@@ -292,13 +291,50 @@ static int smsc47b397_detect(struct i2c_adapter *adapter)
292 return 0; 291 return 0;
293 292
294error_remove: 293error_remove:
295 sysfs_remove_group(&new_client->dev.kobj, &smsc47b397_group); 294 sysfs_remove_group(&dev->kobj, &smsc47b397_group);
296error_detach:
297 i2c_detach_client(new_client);
298error_free: 295error_free:
299 kfree(data); 296 kfree(data);
300error_release: 297error_release:
301 release_region(address, SMSC_EXTENT); 298 release_region(res->start, SMSC_EXTENT);
299 return err;
300}
301
302static int __init smsc47b397_device_add(unsigned short address)
303{
304 struct resource res = {
305 .start = address,
306 .end = address + SMSC_EXTENT - 1,
307 .name = DRVNAME,
308 .flags = IORESOURCE_IO,
309 };
310 int err;
311
312 pdev = platform_device_alloc(DRVNAME, address);
313 if (!pdev) {
314 err = -ENOMEM;
315 printk(KERN_ERR DRVNAME ": Device allocation failed\n");
316 goto exit;
317 }
318
319 err = platform_device_add_resources(pdev, &res, 1);
320 if (err) {
321 printk(KERN_ERR DRVNAME ": Device resource addition failed "
322 "(%d)\n", err);
323 goto exit_device_put;
324 }
325
326 err = platform_device_add(pdev);
327 if (err) {
328 printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
329 err);
330 goto exit_device_put;
331 }
332
333 return 0;
334
335exit_device_put:
336 platform_device_put(pdev);
337exit:
302 return err; 338 return err;
303} 339}
304 340
@@ -320,7 +356,7 @@ static int __init smsc47b397_find(unsigned short *addr)
320 *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) 356 *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8)
321 | superio_inb(SUPERIO_REG_BASE_LSB); 357 | superio_inb(SUPERIO_REG_BASE_LSB);
322 358
323 printk(KERN_INFO "smsc47b397: found SMSC %s " 359 printk(KERN_INFO DRVNAME ": found SMSC %s "
324 "(base address 0x%04x, revision %u)\n", 360 "(base address 0x%04x, revision %u)\n",
325 id == 0x81 ? "SCH5307-NS" : "LPC47B397-NC", *addr, rev); 361 id == 0x81 ? "SCH5307-NS" : "LPC47B397-NC", *addr, rev);
326 362
@@ -330,17 +366,33 @@ static int __init smsc47b397_find(unsigned short *addr)
330 366
331static int __init smsc47b397_init(void) 367static int __init smsc47b397_init(void)
332{ 368{
369 unsigned short address;
333 int ret; 370 int ret;
334 371
335 if ((ret = smsc47b397_find(&address))) 372 if ((ret = smsc47b397_find(&address)))
336 return ret; 373 return ret;
337 374
338 return i2c_isa_add_driver(&smsc47b397_driver); 375 ret = platform_driver_register(&smsc47b397_driver);
376 if (ret)
377 goto exit;
378
379 /* Sets global pdev as a side effect */
380 ret = smsc47b397_device_add(address);
381 if (ret)
382 goto exit_driver;
383
384 return 0;
385
386exit_driver:
387 platform_driver_unregister(&smsc47b397_driver);
388exit:
389 return ret;
339} 390}
340 391
341static void __exit smsc47b397_exit(void) 392static void __exit smsc47b397_exit(void)
342{ 393{
343 i2c_isa_del_driver(&smsc47b397_driver); 394 platform_device_unregister(pdev);
395 platform_driver_unregister(&smsc47b397_driver);
344} 396}
345 397
346MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 398MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");