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>

authored by

Darrick J. Wong and committed by
Linus Torvalds
a412ae3f c0b4e3ab

+557
+31
Documentation/ics932s401
··· 1 + Kernel driver ics932s401 2 + ====================== 3 + 4 + Supported chips: 5 + * IDT ICS932S401 6 + Prefix: 'ics932s401' 7 + Addresses scanned: I2C 0x69 8 + Datasheet: Publically available at the IDT website 9 + 10 + Author: Darrick J. Wong 11 + 12 + Description 13 + ----------- 14 + 15 + This driver implements support for the IDT ICS932S401 chip family. 16 + 17 + This chip has 4 clock outputs--a base clock for the CPU (which is likely 18 + multiplied to get the real CPU clock), a system clock, a PCI clock, a USB 19 + clock, and a reference clock. The driver reports selected and actual 20 + frequency. If spread spectrum mode is enabled, the driver also reports by what 21 + percent the clock signal is being spread, which should be between 0 and -0.5%. 22 + All frequencies are reported in KHz. 23 + 24 + The ICS932S401 monitors all inputs continuously. The driver will not read 25 + the registers more often than once every other second. 26 + 27 + Special Features 28 + ---------------- 29 + 30 + The clocks could be reprogrammed to increase system speed. I will not help you 31 + do this, as you risk damaging your system!
+10
drivers/misc/Kconfig
··· 227 227 To compile this driver as a module, choose M here: the module will 228 228 be called hp-wmi. 229 229 230 + config 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 + 230 240 config MSI_LAPTOP 231 241 tristate "MSI Laptop Extras" 232 242 depends on X86
+1
drivers/misc/Makefile
··· 14 14 obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o 15 15 obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o 16 16 obj-$(CONFIG_HP_WMI) += hp-wmi.o 17 + obj-$(CONFIG_ICS932S401) += ics932s401.o 17 18 obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o 18 19 obj-$(CONFIG_LKDTM) += lkdtm.o 19 20 obj-$(CONFIG_TIFM_CORE) += tifm_core.o
+515
drivers/misc/ics932s401.c
··· 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 */ 31 + static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END }; 32 + 33 + /* Insmod parameters */ 34 + I2C_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 + 74 + static 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 + 98 + struct 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 + 107 + static int ics932s401_probe(struct i2c_client *client, 108 + const struct i2c_device_id *id); 109 + static int ics932s401_detect(struct i2c_client *client, int kind, 110 + struct i2c_board_info *info); 111 + static int ics932s401_remove(struct i2c_client *client); 112 + 113 + static const struct i2c_device_id ics932s401_id[] = { 114 + { "ics932s401", ics932s401 }, 115 + { } 116 + }; 117 + MODULE_DEVICE_TABLE(i2c, ics932s401_id); 118 + 119 + static 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 + 131 + static 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 + 157 + out: 158 + mutex_unlock(&data->lock); 159 + return data; 160 + } 161 + 162 + static 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 */ 175 + static 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 */ 187 + static 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. */ 191 + static 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 + 209 + static 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 + 218 + static 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. */ 247 + static 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 + 265 + static 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 + 274 + static 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. */ 295 + static 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 + 313 + static 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 + 322 + static 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 + 337 + static ssize_t show_value(struct device *dev, 338 + struct device_attribute *devattr, 339 + char *buf); 340 + 341 + static ssize_t show_spread(struct device *dev, 342 + struct device_attribute *devattr, 343 + char *buf); 344 + 345 + static DEVICE_ATTR(spread_enabled, S_IRUGO, show_spread_enabled, NULL); 346 + static DEVICE_ATTR(cpu_clock_selection, S_IRUGO, show_cpu_clock_sel, NULL); 347 + static DEVICE_ATTR(cpu_clock, S_IRUGO, show_cpu_clock, NULL); 348 + static DEVICE_ATTR(src_clock_selection, S_IRUGO, show_src_clock_sel, NULL); 349 + static DEVICE_ATTR(src_clock, S_IRUGO, show_src_clock, NULL); 350 + static DEVICE_ATTR(pci_clock_selection, S_IRUGO, show_pci_clock_sel, NULL); 351 + static DEVICE_ATTR(pci_clock, S_IRUGO, show_pci_clock, NULL); 352 + static DEVICE_ATTR(usb_clock, S_IRUGO, show_value, NULL); 353 + static DEVICE_ATTR(ref_clock, S_IRUGO, show_value, NULL); 354 + static DEVICE_ATTR(cpu_spread, S_IRUGO, show_spread, NULL); 355 + static DEVICE_ATTR(src_spread, S_IRUGO, show_spread, NULL); 356 + 357 + static 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 + 373 + static 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 + 389 + static 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 */ 416 + static 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 + 452 + static 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 + 477 + exit_free: 478 + kfree(data); 479 + exit: 480 + return err; 481 + } 482 + 483 + static 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 + 492 + static int __init ics932s401_init(void) 493 + { 494 + return i2c_add_driver(&ics932s401_driver); 495 + } 496 + 497 + static void __exit ics932s401_exit(void) 498 + { 499 + i2c_del_driver(&ics932s401_driver); 500 + } 501 + 502 + MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); 503 + MODULE_DESCRIPTION("ICS932S401 driver"); 504 + MODULE_LICENSE("GPL"); 505 + 506 + module_init(ics932s401_init); 507 + module_exit(ics932s401_exit); 508 + 509 + /* IBM IntelliStation Z30 */ 510 + MODULE_ALIAS("dmi:bvnIBM:*:rn9228:*"); 511 + MODULE_ALIAS("dmi:bvnIBM:*:rn9232:*"); 512 + 513 + /* IBM x3650/x3550 */ 514 + MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650*"); 515 + MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550*");