aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <djwong@us.ibm.com>2008-11-12 16:27:04 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2008-11-12 20:17:18 -0500
commita412ae3fb90ab49072b82c8cfa1e3e60d2b27005 (patch)
treea145d9346dc1a97eb21dc0c86bcaf62c7cd8c359
parentc0b4e3ab0c769913438aeb078535ff117eeba5fb (diff)
ics932s401: new clock generator chip driver
The ics932s401 is a clock generator chip. This driver allows users to read the current clock outputs. Signed-off-by: Darrick J. Wong <djwong@us.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--Documentation/ics932s40131
-rw-r--r--drivers/misc/Kconfig10
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/ics932s401.c515
4 files changed, 557 insertions, 0 deletions
diff --git a/Documentation/ics932s401 b/Documentation/ics932s401
new file mode 100644
index 00000000000..07a739f406d
--- /dev/null
+++ b/Documentation/ics932s401
@@ -0,0 +1,31 @@
1Kernel driver ics932s401
2======================
3
4Supported chips:
5 * IDT ICS932S401
6 Prefix: 'ics932s401'
7 Addresses scanned: I2C 0x69
8 Datasheet: Publically available at the IDT website
9
10Author: Darrick J. Wong
11
12Description
13-----------
14
15This driver implements support for the IDT ICS932S401 chip family.
16
17This chip has 4 clock outputs--a base clock for the CPU (which is likely
18multiplied to get the real CPU clock), a system clock, a PCI clock, a USB
19clock, and a reference clock. The driver reports selected and actual
20frequency. If spread spectrum mode is enabled, the driver also reports by what
21percent the clock signal is being spread, which should be between 0 and -0.5%.
22All frequencies are reported in KHz.
23
24The ICS932S401 monitors all inputs continuously. The driver will not read
25the registers more often than once every other second.
26
27Special Features
28----------------
29
30The clocks could be reprogrammed to increase system speed. I will not help you
31do this, as you risk damaging your system!
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 4494ad27cbf..dcac7ca7693 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -227,6 +227,16 @@ config HP_WMI
227 To compile this driver as a module, choose M here: the module will 227 To compile this driver as a module, choose M here: the module will
228 be called hp-wmi. 228 be called hp-wmi.
229 229
230config ICS932S401
231 tristate "Integrated Circuits ICS932S401"
232 depends on I2C && EXPERIMENTAL
233 help
234 If you say yes here you get support for the Integrated Circuits
235 ICS932S401 clock control chips.
236
237 This driver can also be built as a module. If so, the module
238 will be called ics932s401.
239
230config MSI_LAPTOP 240config MSI_LAPTOP
231 tristate "MSI Laptop Extras" 241 tristate "MSI Laptop Extras"
232 depends on X86 242 depends on X86
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 909e2468cdc..bb14633d136 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
14obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o 14obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
15obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o 15obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
16obj-$(CONFIG_HP_WMI) += hp-wmi.o 16obj-$(CONFIG_HP_WMI) += hp-wmi.o
17obj-$(CONFIG_ICS932S401) += ics932s401.o
17obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o 18obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
18obj-$(CONFIG_LKDTM) += lkdtm.o 19obj-$(CONFIG_LKDTM) += lkdtm.o
19obj-$(CONFIG_TIFM_CORE) += tifm_core.o 20obj-$(CONFIG_TIFM_CORE) += tifm_core.o
diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c
new file mode 100644
index 00000000000..6e43ab4231a
--- /dev/null
+++ b/drivers/misc/ics932s401.c
@@ -0,0 +1,515 @@
1/*
2 * A driver for the Integrated Circuits ICS932S401
3 * Copyright (C) 2008 IBM
4 *
5 * Author: Darrick J. Wong <djwong@us.ibm.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/module.h>
23#include <linux/jiffies.h>
24#include <linux/i2c.h>
25#include <linux/err.h>
26#include <linux/mutex.h>
27#include <linux/delay.h>
28#include <linux/log2.h>
29
30/* Addresses to scan */
31static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END };
32
33/* Insmod parameters */
34I2C_CLIENT_INSMOD_1(ics932s401);
35
36/* ICS932S401 registers */
37#define ICS932S401_REG_CFG2 0x01
38#define ICS932S401_CFG1_SPREAD 0x01
39#define ICS932S401_REG_CFG7 0x06
40#define ICS932S401_FS_MASK 0x07
41#define ICS932S401_REG_VENDOR_REV 0x07
42#define ICS932S401_VENDOR 1
43#define ICS932S401_VENDOR_MASK 0x0F
44#define ICS932S401_REV 4
45#define ICS932S401_REV_SHIFT 4
46#define ICS932S401_REG_DEVICE 0x09
47#define ICS932S401_DEVICE 11
48#define ICS932S401_REG_CTRL 0x0A
49#define ICS932S401_MN_ENABLED 0x80
50#define ICS932S401_CPU_ALT 0x04
51#define ICS932S401_SRC_ALT 0x08
52#define ICS932S401_REG_CPU_M_CTRL 0x0B
53#define ICS932S401_M_MASK 0x3F
54#define ICS932S401_REG_CPU_N_CTRL 0x0C
55#define ICS932S401_REG_CPU_SPREAD1 0x0D
56#define ICS932S401_REG_CPU_SPREAD2 0x0E
57#define ICS932S401_SPREAD_MASK 0x7FFF
58#define ICS932S401_REG_SRC_M_CTRL 0x0F
59#define ICS932S401_REG_SRC_N_CTRL 0x10
60#define ICS932S401_REG_SRC_SPREAD1 0x11
61#define ICS932S401_REG_SRC_SPREAD2 0x12
62#define ICS932S401_REG_CPU_DIVISOR 0x13
63#define ICS932S401_CPU_DIVISOR_SHIFT 4
64#define ICS932S401_REG_PCISRC_DIVISOR 0x14
65#define ICS932S401_SRC_DIVISOR_MASK 0x0F
66#define ICS932S401_PCI_DIVISOR_SHIFT 4
67
68/* Base clock is 14.318MHz */
69#define BASE_CLOCK 14318
70
71#define NUM_REGS 21
72#define NUM_MIRRORED_REGS 15
73
74static int regs_to_copy[NUM_MIRRORED_REGS] = {
75 ICS932S401_REG_CFG2,
76 ICS932S401_REG_CFG7,
77 ICS932S401_REG_VENDOR_REV,
78 ICS932S401_REG_DEVICE,
79 ICS932S401_REG_CTRL,
80 ICS932S401_REG_CPU_M_CTRL,
81 ICS932S401_REG_CPU_N_CTRL,
82 ICS932S401_REG_CPU_SPREAD1,
83 ICS932S401_REG_CPU_SPREAD2,
84 ICS932S401_REG_SRC_M_CTRL,
85 ICS932S401_REG_SRC_N_CTRL,
86 ICS932S401_REG_SRC_SPREAD1,
87 ICS932S401_REG_SRC_SPREAD2,
88 ICS932S401_REG_CPU_DIVISOR,
89 ICS932S401_REG_PCISRC_DIVISOR,
90};
91
92/* How often do we reread sensors values? (In jiffies) */
93#define SENSOR_REFRESH_INTERVAL (2 * HZ)
94
95/* How often do we reread sensor limit values? (In jiffies) */
96#define LIMIT_REFRESH_INTERVAL (60 * HZ)
97
98struct ics932s401_data {
99 struct attribute_group attrs;
100 struct mutex lock;
101 char sensors_valid;
102 unsigned long sensors_last_updated; /* In jiffies */
103
104 u8 regs[NUM_REGS];
105};
106
107static int ics932s401_probe(struct i2c_client *client,
108 const struct i2c_device_id *id);
109static int ics932s401_detect(struct i2c_client *client, int kind,
110 struct i2c_board_info *info);
111static int ics932s401_remove(struct i2c_client *client);
112
113static const struct i2c_device_id ics932s401_id[] = {
114 { "ics932s401", ics932s401 },
115 { }
116};
117MODULE_DEVICE_TABLE(i2c, ics932s401_id);
118
119static struct i2c_driver ics932s401_driver = {
120 .class = I2C_CLASS_HWMON,
121 .driver = {
122 .name = "ics932s401",
123 },
124 .probe = ics932s401_probe,
125 .remove = ics932s401_remove,
126 .id_table = ics932s401_id,
127 .detect = ics932s401_detect,
128 .address_data = &addr_data,
129};
130
131static struct ics932s401_data *ics932s401_update_device(struct device *dev)
132{
133 struct i2c_client *client = to_i2c_client(dev);
134 struct ics932s401_data *data = i2c_get_clientdata(client);
135 unsigned long local_jiffies = jiffies;
136 int i, temp;
137
138 mutex_lock(&data->lock);
139 if (time_before(local_jiffies, data->sensors_last_updated +
140 SENSOR_REFRESH_INTERVAL)
141 && data->sensors_valid)
142 goto out;
143
144 /*
145 * Each register must be read as a word and then right shifted 8 bits.
146 * Not really sure why this is; setting the "byte count programming"
147 * register to 1 does not fix this problem.
148 */
149 for (i = 0; i < NUM_MIRRORED_REGS; i++) {
150 temp = i2c_smbus_read_word_data(client, regs_to_copy[i]);
151 data->regs[regs_to_copy[i]] = temp >> 8;
152 }
153
154 data->sensors_last_updated = local_jiffies;
155 data->sensors_valid = 1;
156
157out:
158 mutex_unlock(&data->lock);
159 return data;
160}
161
162static ssize_t show_spread_enabled(struct device *dev,
163 struct device_attribute *devattr,
164 char *buf)
165{
166 struct ics932s401_data *data = ics932s401_update_device(dev);
167
168 if (data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD)
169 return sprintf(buf, "1\n");
170
171 return sprintf(buf, "0\n");
172}
173
174/* bit to cpu khz map */
175static const int fs_speeds[] = {
176 266666,
177 133333,
178 200000,
179 166666,
180 333333,
181 100000,
182 400000,
183 0,
184};
185
186/* clock divisor map */
187static const int divisors[] = {2, 3, 5, 15, 4, 6, 10, 30, 8, 12, 20, 60, 16,
188 24, 40, 120};
189
190/* Calculate CPU frequency from the M/N registers. */
191static int calculate_cpu_freq(struct ics932s401_data *data)
192{
193 int m, n, freq;
194
195 m = data->regs[ICS932S401_REG_CPU_M_CTRL] & ICS932S401_M_MASK;
196 n = data->regs[ICS932S401_REG_CPU_N_CTRL];
197
198 /* Pull in bits 8 & 9 from the M register */
199 n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x80) << 1;
200 n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x40) << 3;
201
202 freq = BASE_CLOCK * (n + 8) / (m + 2);
203 freq /= divisors[data->regs[ICS932S401_REG_CPU_DIVISOR] >>
204 ICS932S401_CPU_DIVISOR_SHIFT];
205
206 return freq;
207}
208
209static ssize_t show_cpu_clock(struct device *dev,
210 struct device_attribute *devattr,
211 char *buf)
212{
213 struct ics932s401_data *data = ics932s401_update_device(dev);
214
215 return sprintf(buf, "%d\n", calculate_cpu_freq(data));
216}
217
218static ssize_t show_cpu_clock_sel(struct device *dev,
219 struct device_attribute *devattr,
220 char *buf)
221{
222 struct ics932s401_data *data = ics932s401_update_device(dev);
223 int freq;
224
225 if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED)
226 freq = calculate_cpu_freq(data);
227 else {
228 /* Freq is neatly wrapped up for us */
229 int fid = data->regs[ICS932S401_REG_CFG7] & ICS932S401_FS_MASK;
230 freq = fs_speeds[fid];
231 if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT) {
232 switch (freq) {
233 case 166666:
234 freq = 160000;
235 break;
236 case 333333:
237 freq = 320000;
238 break;
239 }
240 }
241 }
242
243 return sprintf(buf, "%d\n", freq);
244}
245
246/* Calculate SRC frequency from the M/N registers. */
247static int calculate_src_freq(struct ics932s401_data *data)
248{
249 int m, n, freq;
250
251 m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK;
252 n = data->regs[ICS932S401_REG_SRC_N_CTRL];
253
254 /* Pull in bits 8 & 9 from the M register */
255 n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1;
256 n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3;
257
258 freq = BASE_CLOCK * (n + 8) / (m + 2);
259 freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] &
260 ICS932S401_SRC_DIVISOR_MASK];
261
262 return freq;
263}
264
265static ssize_t show_src_clock(struct device *dev,
266 struct device_attribute *devattr,
267 char *buf)
268{
269 struct ics932s401_data *data = ics932s401_update_device(dev);
270
271 return sprintf(buf, "%d\n", calculate_src_freq(data));
272}
273
274static ssize_t show_src_clock_sel(struct device *dev,
275 struct device_attribute *devattr,
276 char *buf)
277{
278 struct ics932s401_data *data = ics932s401_update_device(dev);
279 int freq;
280
281 if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED)
282 freq = calculate_src_freq(data);
283 else
284 /* Freq is neatly wrapped up for us */
285 if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT &&
286 data->regs[ICS932S401_REG_CTRL] & ICS932S401_SRC_ALT)
287 freq = 96000;
288 else
289 freq = 100000;
290
291 return sprintf(buf, "%d\n", freq);
292}
293
294/* Calculate PCI frequency from the SRC M/N registers. */
295static int calculate_pci_freq(struct ics932s401_data *data)
296{
297 int m, n, freq;
298
299 m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK;
300 n = data->regs[ICS932S401_REG_SRC_N_CTRL];
301
302 /* Pull in bits 8 & 9 from the M register */
303 n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1;
304 n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3;
305
306 freq = BASE_CLOCK * (n + 8) / (m + 2);
307 freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] >>
308 ICS932S401_PCI_DIVISOR_SHIFT];
309
310 return freq;
311}
312
313static ssize_t show_pci_clock(struct device *dev,
314 struct device_attribute *devattr,
315 char *buf)
316{
317 struct ics932s401_data *data = ics932s401_update_device(dev);
318
319 return sprintf(buf, "%d\n", calculate_pci_freq(data));
320}
321
322static ssize_t show_pci_clock_sel(struct device *dev,
323 struct device_attribute *devattr,
324 char *buf)
325{
326 struct ics932s401_data *data = ics932s401_update_device(dev);
327 int freq;
328
329 if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED)
330 freq = calculate_pci_freq(data);
331 else
332 freq = 33333;
333
334 return sprintf(buf, "%d\n", freq);
335}
336
337static ssize_t show_value(struct device *dev,
338 struct device_attribute *devattr,
339 char *buf);
340
341static ssize_t show_spread(struct device *dev,
342 struct device_attribute *devattr,
343 char *buf);
344
345static DEVICE_ATTR(spread_enabled, S_IRUGO, show_spread_enabled, NULL);
346static DEVICE_ATTR(cpu_clock_selection, S_IRUGO, show_cpu_clock_sel, NULL);
347static DEVICE_ATTR(cpu_clock, S_IRUGO, show_cpu_clock, NULL);
348static DEVICE_ATTR(src_clock_selection, S_IRUGO, show_src_clock_sel, NULL);
349static DEVICE_ATTR(src_clock, S_IRUGO, show_src_clock, NULL);
350static DEVICE_ATTR(pci_clock_selection, S_IRUGO, show_pci_clock_sel, NULL);
351static DEVICE_ATTR(pci_clock, S_IRUGO, show_pci_clock, NULL);
352static DEVICE_ATTR(usb_clock, S_IRUGO, show_value, NULL);
353static DEVICE_ATTR(ref_clock, S_IRUGO, show_value, NULL);
354static DEVICE_ATTR(cpu_spread, S_IRUGO, show_spread, NULL);
355static DEVICE_ATTR(src_spread, S_IRUGO, show_spread, NULL);
356
357static struct attribute *ics932s401_attr[] =
358{
359 &dev_attr_spread_enabled.attr,
360 &dev_attr_cpu_clock_selection.attr,
361 &dev_attr_cpu_clock.attr,
362 &dev_attr_src_clock_selection.attr,
363 &dev_attr_src_clock.attr,
364 &dev_attr_pci_clock_selection.attr,
365 &dev_attr_pci_clock.attr,
366 &dev_attr_usb_clock.attr,
367 &dev_attr_ref_clock.attr,
368 &dev_attr_cpu_spread.attr,
369 &dev_attr_src_spread.attr,
370 NULL
371};
372
373static ssize_t show_value(struct device *dev,
374 struct device_attribute *devattr,
375 char *buf)
376{
377 int x;
378
379 if (devattr == &dev_attr_usb_clock)
380 x = 48000;
381 else if (devattr == &dev_attr_ref_clock)
382 x = BASE_CLOCK;
383 else
384 BUG();
385
386 return sprintf(buf, "%d\n", x);
387}
388
389static ssize_t show_spread(struct device *dev,
390 struct device_attribute *devattr,
391 char *buf)
392{
393 struct ics932s401_data *data = ics932s401_update_device(dev);
394 int reg;
395 unsigned long val;
396
397 if (!(data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD))
398 return sprintf(buf, "0%%\n");
399
400 if (devattr == &dev_attr_src_spread)
401 reg = ICS932S401_REG_SRC_SPREAD1;
402 else if (devattr == &dev_attr_cpu_spread)
403 reg = ICS932S401_REG_CPU_SPREAD1;
404 else
405 BUG();
406
407 val = data->regs[reg] | (data->regs[reg + 1] << 8);
408 val &= ICS932S401_SPREAD_MASK;
409
410 /* Scale 0..2^14 to -0.5. */
411 val = 500000 * val / 16384;
412 return sprintf(buf, "-0.%lu%%\n", val);
413}
414
415/* Return 0 if detection is successful, -ENODEV otherwise */
416static int ics932s401_detect(struct i2c_client *client, int kind,
417 struct i2c_board_info *info)
418{
419 struct i2c_adapter *adapter = client->adapter;
420
421 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
422 return -ENODEV;
423
424 if (kind <= 0) {
425 int vendor, device, revision;
426
427 vendor = i2c_smbus_read_word_data(client,
428 ICS932S401_REG_VENDOR_REV);
429 vendor >>= 8;
430 revision = vendor >> ICS932S401_REV_SHIFT;
431 vendor &= ICS932S401_VENDOR_MASK;
432 if (vendor != ICS932S401_VENDOR)
433 return -ENODEV;
434
435 device = i2c_smbus_read_word_data(client,
436 ICS932S401_REG_DEVICE);
437 device >>= 8;
438 if (device != ICS932S401_DEVICE)
439 return -ENODEV;
440
441 if (revision != ICS932S401_REV)
442 dev_info(&adapter->dev, "Unknown revision %d\n",
443 revision);
444 } else
445 dev_dbg(&adapter->dev, "detection forced\n");
446
447 strlcpy(info->type, "ics932s401", I2C_NAME_SIZE);
448
449 return 0;
450}
451
452static int ics932s401_probe(struct i2c_client *client,
453 const struct i2c_device_id *id)
454{
455 struct ics932s401_data *data;
456 int err;
457
458 data = kzalloc(sizeof(struct ics932s401_data), GFP_KERNEL);
459 if (!data) {
460 err = -ENOMEM;
461 goto exit;
462 }
463
464 i2c_set_clientdata(client, data);
465 mutex_init(&data->lock);
466
467 dev_info(&client->dev, "%s chip found\n", client->name);
468
469 /* Register sysfs hooks */
470 data->attrs.attrs = ics932s401_attr;
471 err = sysfs_create_group(&client->dev.kobj, &data->attrs);
472 if (err)
473 goto exit_free;
474
475 return 0;
476
477exit_free:
478 kfree(data);
479exit:
480 return err;
481}
482
483static int ics932s401_remove(struct i2c_client *client)
484{
485 struct ics932s401_data *data = i2c_get_clientdata(client);
486
487 sysfs_remove_group(&client->dev.kobj, &data->attrs);
488 kfree(data);
489 return 0;
490}
491
492static int __init ics932s401_init(void)
493{
494 return i2c_add_driver(&ics932s401_driver);
495}
496
497static void __exit ics932s401_exit(void)
498{
499 i2c_del_driver(&ics932s401_driver);
500}
501
502MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
503MODULE_DESCRIPTION("ICS932S401 driver");
504MODULE_LICENSE("GPL");
505
506module_init(ics932s401_init);
507module_exit(ics932s401_exit);
508
509/* IBM IntelliStation Z30 */
510MODULE_ALIAS("dmi:bvnIBM:*:rn9228:*");
511MODULE_ALIAS("dmi:bvnIBM:*:rn9232:*");
512
513/* IBM x3650/x3550 */
514MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650*");
515MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550*");