Blame view
drivers/hwmon/smsc47b397.c
8.11 KB
74ba9207e treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4c Linux-2.6.12-rc2 |
2 |
/* |
2a52dd667 hwmon: (smsc47b39... |
3 4 5 6 7 8 9 10 11 12 |
* smsc47b397.c - Part of lm_sensors, Linux kernel modules * for hardware monitoring * * Supports the SMSC LPC47B397-NC Super-I/O chip. * * Author/Maintainer: Mark M. Hoffman <mhoffman@lightlink.com> * Copyright (C) 2004 Utilitek Systems, Inc. * * derived in part from smsc47m1.c: * Copyright (C) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com> |
7c81c60f3 Update Jean Delva... |
13 |
* Copyright (C) 2004 Jean Delvare <jdelvare@suse.de> |
2a52dd667 hwmon: (smsc47b39... |
14 |
*/ |
1da177e4c Linux-2.6.12-rc2 |
15 |
|
bf1a85eff hwmon: (smsc47b39... |
16 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
1da177e4c Linux-2.6.12-rc2 |
17 18 19 20 |
#include <linux/module.h> #include <linux/slab.h> #include <linux/ioport.h> #include <linux/jiffies.h> |
292fc1a5f hwmon/smsc47b397:... |
21 |
#include <linux/platform_device.h> |
943b0830c [PATCH] I2C hwmon... |
22 |
#include <linux/hwmon.h> |
3659a0178 hwmon/smsc47b397:... |
23 |
#include <linux/hwmon-sysfs.h> |
943b0830c [PATCH] I2C hwmon... |
24 |
#include <linux/err.h> |
1da177e4c Linux-2.6.12-rc2 |
25 |
#include <linux/init.h> |
9a61bf630 [PATCH] hwmon: Se... |
26 |
#include <linux/mutex.h> |
b9acb64a3 hwmon: Check for ... |
27 |
#include <linux/acpi.h> |
6055fae8a hwmon: Include <l... |
28 |
#include <linux/io.h> |
1da177e4c Linux-2.6.12-rc2 |
29 |
|
67b671bce hwmon: Let the us... |
30 31 32 |
static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); |
292fc1a5f hwmon/smsc47b397:... |
33 34 35 |
static struct platform_device *pdev; #define DRVNAME "smsc47b397" |
1da177e4c Linux-2.6.12-rc2 |
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
/* 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); } /* select superio logical device */ static inline void superio_select(int ld) { superio_outb(0x07, ld); } |
8c0826756 hwmon: (smsc47b39... |
59 |
static inline int superio_enter(void) |
1da177e4c Linux-2.6.12-rc2 |
60 |
{ |
8c0826756 hwmon: (smsc47b39... |
61 62 |
if (!request_muxed_region(REG, 2, DRVNAME)) return -EBUSY; |
1da177e4c Linux-2.6.12-rc2 |
63 |
outb(0x55, REG); |
8c0826756 hwmon: (smsc47b39... |
64 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
65 66 67 68 69 |
} static inline void superio_exit(void) { outb(0xAA, REG); |
8c0826756 hwmon: (smsc47b39... |
70 |
release_region(REG, 2); |
1da177e4c Linux-2.6.12-rc2 |
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
} #define SUPERIO_REG_DEVID 0x20 #define SUPERIO_REG_DEVREV 0x21 #define SUPERIO_REG_BASE_MSB 0x60 #define SUPERIO_REG_BASE_LSB 0x61 #define SUPERIO_REG_LD8 0x08 #define SMSC_EXTENT 0x02 /* 0 <= nr <= 3 */ static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80}; #define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)]) /* 0 <= nr <= 3 */ #define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr)) #define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr)) struct smsc47b397_data { |
292fc1a5f hwmon/smsc47b397:... |
90 |
unsigned short addr; |
9a61bf630 [PATCH] hwmon: Se... |
91 |
struct mutex lock; |
1da177e4c Linux-2.6.12-rc2 |
92 |
|
9a61bf630 [PATCH] hwmon: Se... |
93 |
struct mutex update_lock; |
1da177e4c Linux-2.6.12-rc2 |
94 95 96 97 98 99 100 |
unsigned long last_updated; /* in jiffies */ int valid; /* register values */ u16 fan[4]; u8 temp[4]; }; |
371f2e083 hwmon: (smsc47b39... |
101 |
static int smsc47b397_read_value(struct smsc47b397_data *data, u8 reg) |
1da177e4c Linux-2.6.12-rc2 |
102 |
{ |
1da177e4c Linux-2.6.12-rc2 |
103 |
int res; |
9a61bf630 [PATCH] hwmon: Se... |
104 |
mutex_lock(&data->lock); |
292fc1a5f hwmon/smsc47b397:... |
105 106 |
outb(reg, data->addr); res = inb_p(data->addr + 1); |
9a61bf630 [PATCH] hwmon: Se... |
107 |
mutex_unlock(&data->lock); |
1da177e4c Linux-2.6.12-rc2 |
108 109 110 111 112 |
return res; } static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) { |
292fc1a5f hwmon/smsc47b397:... |
113 |
struct smsc47b397_data *data = dev_get_drvdata(dev); |
1da177e4c Linux-2.6.12-rc2 |
114 |
int i; |
9a61bf630 [PATCH] hwmon: Se... |
115 |
mutex_lock(&data->update_lock); |
1da177e4c Linux-2.6.12-rc2 |
116 117 |
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { |
292fc1a5f hwmon/smsc47b397:... |
118 119 |
dev_dbg(dev, "starting device update... "); |
1da177e4c Linux-2.6.12-rc2 |
120 121 122 |
/* 4 temperature inputs, 4 fan inputs */ for (i = 0; i < 4; i++) { |
292fc1a5f hwmon/smsc47b397:... |
123 |
data->temp[i] = smsc47b397_read_value(data, |
1da177e4c Linux-2.6.12-rc2 |
124 125 126 |
SMSC47B397_REG_TEMP(i)); /* must read LSB first */ |
292fc1a5f hwmon/smsc47b397:... |
127 |
data->fan[i] = smsc47b397_read_value(data, |
1da177e4c Linux-2.6.12-rc2 |
128 |
SMSC47B397_REG_FAN_LSB(i)); |
292fc1a5f hwmon/smsc47b397:... |
129 |
data->fan[i] |= smsc47b397_read_value(data, |
1da177e4c Linux-2.6.12-rc2 |
130 131 132 133 134 |
SMSC47B397_REG_FAN_MSB(i)) << 8; } data->last_updated = jiffies; data->valid = 1; |
292fc1a5f hwmon/smsc47b397:... |
135 136 |
dev_dbg(dev, "... device update complete "); |
1da177e4c Linux-2.6.12-rc2 |
137 |
} |
9a61bf630 [PATCH] hwmon: Se... |
138 |
mutex_unlock(&data->update_lock); |
1da177e4c Linux-2.6.12-rc2 |
139 140 141 |
return data; } |
2a52dd667 hwmon: (smsc47b39... |
142 143 144 145 |
/* * TEMP: 0.001C/bit (-128C to +127C) * REG: 1C/bit, two's complement */ |
1da177e4c Linux-2.6.12-rc2 |
146 147 148 149 |
static int temp_from_reg(u8 reg) { return (s8)reg * 1000; } |
8721bdecd hwmon: (smsc47b39... |
150 151 |
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, char *buf) |
1da177e4c Linux-2.6.12-rc2 |
152 |
{ |
3659a0178 hwmon/smsc47b397:... |
153 |
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
1da177e4c Linux-2.6.12-rc2 |
154 |
struct smsc47b397_data *data = smsc47b397_update_device(dev); |
3659a0178 hwmon/smsc47b397:... |
155 156 |
return sprintf(buf, "%d ", temp_from_reg(data->temp[attr->index])); |
1da177e4c Linux-2.6.12-rc2 |
157 |
} |
8721bdecd hwmon: (smsc47b39... |
158 159 160 161 |
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); |
1da177e4c Linux-2.6.12-rc2 |
162 |
|
2a52dd667 hwmon: (smsc47b39... |
163 164 165 166 |
/* * FAN: 1 RPM/bit * REG: count of 90kHz pulses / revolution */ |
1da177e4c Linux-2.6.12-rc2 |
167 168 |
static int fan_from_reg(u16 reg) { |
90205c6cb hwmon/smsc47b397:... |
169 170 |
if (reg == 0 || reg == 0xffff) return 0; |
1da177e4c Linux-2.6.12-rc2 |
171 172 |
return 90000 * 60 / reg; } |
8721bdecd hwmon: (smsc47b39... |
173 174 |
static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, char *buf) |
1da177e4c Linux-2.6.12-rc2 |
175 |
{ |
3659a0178 hwmon/smsc47b397:... |
176 177 178 179 |
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct smsc47b397_data *data = smsc47b397_update_device(dev); return sprintf(buf, "%d ", fan_from_reg(data->fan[attr->index])); |
1da177e4c Linux-2.6.12-rc2 |
180 |
} |
8721bdecd hwmon: (smsc47b39... |
181 182 183 184 |
static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2); static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3); |
1da177e4c Linux-2.6.12-rc2 |
185 |
|
9b993e366 hwmon: (smsc47b39... |
186 |
static struct attribute *smsc47b397_attrs[] = { |
3659a0178 hwmon/smsc47b397:... |
187 188 189 190 191 192 193 194 |
&sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp4_input.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan4_input.dev_attr.attr, |
c1685f61b hwmon: Fix unchec... |
195 196 197 |
NULL }; |
9b993e366 hwmon: (smsc47b39... |
198 |
ATTRIBUTE_GROUPS(smsc47b397); |
1da177e4c Linux-2.6.12-rc2 |
199 |
|
292fc1a5f hwmon/smsc47b397:... |
200 |
static int smsc47b397_probe(struct platform_device *pdev); |
2d8672c5a [PATCH] I2C: Sepa... |
201 |
|
292fc1a5f hwmon/smsc47b397:... |
202 |
static struct platform_driver smsc47b397_driver = { |
cdaf79349 [PATCH] i2c: Drop... |
203 |
.driver = { |
292fc1a5f hwmon/smsc47b397:... |
204 |
.name = DRVNAME, |
cdaf79349 [PATCH] i2c: Drop... |
205 |
}, |
292fc1a5f hwmon/smsc47b397:... |
206 |
.probe = smsc47b397_probe, |
1da177e4c Linux-2.6.12-rc2 |
207 |
}; |
6c931ae1c hwmon: remove use... |
208 |
static int smsc47b397_probe(struct platform_device *pdev) |
1da177e4c Linux-2.6.12-rc2 |
209 |
{ |
292fc1a5f hwmon/smsc47b397:... |
210 |
struct device *dev = &pdev->dev; |
1da177e4c Linux-2.6.12-rc2 |
211 |
struct smsc47b397_data *data; |
9b993e366 hwmon: (smsc47b39... |
212 |
struct device *hwmon_dev; |
292fc1a5f hwmon/smsc47b397:... |
213 |
struct resource *res; |
1da177e4c Linux-2.6.12-rc2 |
214 |
|
292fc1a5f hwmon/smsc47b397:... |
215 |
res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
e43d5fefa hwmon: (smsc47b39... |
216 217 |
if (!devm_request_region(dev, res->start, SMSC_EXTENT, smsc47b397_driver.driver.name)) { |
292fc1a5f hwmon/smsc47b397:... |
218 219 220 221 |
dev_err(dev, "Region 0x%lx-0x%lx already in use! ", (unsigned long)res->start, (unsigned long)res->start + SMSC_EXTENT - 1); |
1da177e4c Linux-2.6.12-rc2 |
222 223 |
return -EBUSY; } |
e43d5fefa hwmon: (smsc47b39... |
224 225 226 |
data = devm_kzalloc(dev, sizeof(struct smsc47b397_data), GFP_KERNEL); if (!data) return -ENOMEM; |
1da177e4c Linux-2.6.12-rc2 |
227 |
|
292fc1a5f hwmon/smsc47b397:... |
228 |
data->addr = res->start; |
9a61bf630 [PATCH] hwmon: Se... |
229 |
mutex_init(&data->lock); |
9a61bf630 [PATCH] hwmon: Se... |
230 |
mutex_init(&data->update_lock); |
1da177e4c Linux-2.6.12-rc2 |
231 |
|
9b993e366 hwmon: (smsc47b39... |
232 233 234 235 |
hwmon_dev = devm_hwmon_device_register_with_groups(dev, "smsc47b397", data, smsc47b397_groups); return PTR_ERR_OR_ZERO(hwmon_dev); |
292fc1a5f hwmon/smsc47b397:... |
236 237 238 239 240 241 242 243 244 245 246 |
} static int __init smsc47b397_device_add(unsigned short address) { struct resource res = { .start = address, .end = address + SMSC_EXTENT - 1, .name = DRVNAME, .flags = IORESOURCE_IO, }; int err; |
b9acb64a3 hwmon: Check for ... |
247 248 249 |
err = acpi_check_resource_conflict(&res); if (err) goto exit; |
292fc1a5f hwmon/smsc47b397:... |
250 251 252 |
pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; |
bf1a85eff hwmon: (smsc47b39... |
253 254 |
pr_err("Device allocation failed "); |
292fc1a5f hwmon/smsc47b397:... |
255 256 257 258 259 |
goto exit; } err = platform_device_add_resources(pdev, &res, 1); if (err) { |
bf1a85eff hwmon: (smsc47b39... |
260 261 |
pr_err("Device resource addition failed (%d) ", err); |
292fc1a5f hwmon/smsc47b397:... |
262 263 264 265 266 |
goto exit_device_put; } err = platform_device_add(pdev); if (err) { |
bf1a85eff hwmon: (smsc47b39... |
267 268 |
pr_err("Device addition failed (%d) ", err); |
292fc1a5f hwmon/smsc47b397:... |
269 270 271 272 273 274 275 276 |
goto exit_device_put; } return 0; exit_device_put: platform_device_put(pdev); exit: |
1da177e4c Linux-2.6.12-rc2 |
277 278 |
return err; } |
8528e07ed hwmon: (smsc47b39... |
279 |
static int __init smsc47b397_find(void) |
1da177e4c Linux-2.6.12-rc2 |
280 281 |
{ u8 id, rev; |
809307768 hwmon: (smsc47b39... |
282 |
char *name; |
8528e07ed hwmon: (smsc47b39... |
283 |
unsigned short addr; |
8c0826756 hwmon: (smsc47b39... |
284 285 286 287 288 |
int err; err = superio_enter(); if (err) return err; |
1da177e4c Linux-2.6.12-rc2 |
289 |
|
67b671bce hwmon: Let the us... |
290 |
id = force_id ? force_id : superio_inb(SUPERIO_REG_DEVID); |
1da177e4c Linux-2.6.12-rc2 |
291 |
|
371f2e083 hwmon: (smsc47b39... |
292 |
switch (id) { |
809307768 hwmon: (smsc47b39... |
293 294 295 296 297 298 299 300 301 302 303 |
case 0x81: name = "SCH5307-NS"; break; case 0x6f: name = "LPC47B397-NC"; break; case 0x85: case 0x8c: name = "SCH5317"; break; default: |
1da177e4c Linux-2.6.12-rc2 |
304 305 306 307 308 309 310 |
superio_exit(); return -ENODEV; } rev = superio_inb(SUPERIO_REG_DEVREV); superio_select(SUPERIO_REG_LD8); |
8528e07ed hwmon: (smsc47b39... |
311 |
addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) |
1da177e4c Linux-2.6.12-rc2 |
312 |
| superio_inb(SUPERIO_REG_BASE_LSB); |
bf1a85eff hwmon: (smsc47b39... |
313 314 |
pr_info("found SMSC %s (base address 0x%04x, revision %u) ", |
8528e07ed hwmon: (smsc47b39... |
315 |
name, addr, rev); |
1da177e4c Linux-2.6.12-rc2 |
316 317 |
superio_exit(); |
8528e07ed hwmon: (smsc47b39... |
318 |
return addr; |
1da177e4c Linux-2.6.12-rc2 |
319 320 321 322 |
} static int __init smsc47b397_init(void) { |
292fc1a5f hwmon/smsc47b397:... |
323 |
unsigned short address; |
1da177e4c Linux-2.6.12-rc2 |
324 |
int ret; |
8528e07ed hwmon: (smsc47b39... |
325 326 |
ret = smsc47b397_find(); if (ret < 0) |
1da177e4c Linux-2.6.12-rc2 |
327 |
return ret; |
8528e07ed hwmon: (smsc47b39... |
328 |
address = ret; |
1da177e4c Linux-2.6.12-rc2 |
329 |
|
292fc1a5f hwmon/smsc47b397:... |
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
ret = platform_driver_register(&smsc47b397_driver); if (ret) goto exit; /* Sets global pdev as a side effect */ ret = smsc47b397_device_add(address); if (ret) goto exit_driver; return 0; exit_driver: platform_driver_unregister(&smsc47b397_driver); exit: return ret; |
1da177e4c Linux-2.6.12-rc2 |
345 346 347 348 |
} static void __exit smsc47b397_exit(void) { |
292fc1a5f hwmon/smsc47b397:... |
349 350 |
platform_device_unregister(pdev); platform_driver_unregister(&smsc47b397_driver); |
1da177e4c Linux-2.6.12-rc2 |
351 352 353 354 355 356 357 358 |
} MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); MODULE_DESCRIPTION("SMSC LPC47B397 driver"); MODULE_LICENSE("GPL"); module_init(smsc47b397_init); module_exit(smsc47b397_exit); |