| 1 | |
| 2 | |
| 3 | * A driver for the Integrated Circuits ICS932S401
|
| 4 | * Copyright (C) 2008 IBM
|
| 5 | *
|
| 6 | * Author: Darrick J. Wong <[email protected]>
|
| 7 |
|
| 8 |
|
| 9 | #include <linux/module.h>
|
| 10 | #include <linux/jiffies.h>
|
| 11 | #include <linux/i2c.h>
|
| 12 | #include <linux/err.h>
|
| 13 | #include <linux/mutex.h>
|
| 14 | #include <linux/delay.h>
|
| 15 | #include <linux/log2.h>
|
| 16 | #include <linux/slab.h>
|
| 17 |
|
| 18 |
|
| 19 | static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END };
|
| 20 |
|
| 21 |
|
| 22 | #define ICS932S401_REG_CFG2 0x01
|
| 23 | #define ICS932S401_CFG1_SPREAD 0x01
|
| 24 | #define ICS932S401_REG_CFG7 0x06
|
| 25 | #define ICS932S401_FS_MASK 0x07
|
| 26 | #define ICS932S401_REG_VENDOR_REV 0x07
|
| 27 | #define ICS932S401_VENDOR 1
|
| 28 | #define ICS932S401_VENDOR_MASK 0x0F
|
| 29 | #define ICS932S401_REV 4
|
| 30 | #define ICS932S401_REV_SHIFT 4
|
| 31 | #define ICS932S401_REG_DEVICE 0x09
|
| 32 | #define ICS932S401_DEVICE 11
|
| 33 | #define ICS932S401_REG_CTRL 0x0A
|
| 34 | #define ICS932S401_MN_ENABLED 0x80
|
| 35 | #define ICS932S401_CPU_ALT 0x04
|
| 36 | #define ICS932S401_SRC_ALT 0x08
|
| 37 | #define ICS932S401_REG_CPU_M_CTRL 0x0B
|
| 38 | #define ICS932S401_M_MASK 0x3F
|
| 39 | #define ICS932S401_REG_CPU_N_CTRL 0x0C
|
| 40 | #define ICS932S401_REG_CPU_SPREAD1 0x0D
|
| 41 | #define ICS932S401_REG_CPU_SPREAD2 0x0E
|
| 42 | #define ICS932S401_SPREAD_MASK 0x7FFF
|
| 43 | #define ICS932S401_REG_SRC_M_CTRL 0x0F
|
| 44 | #define ICS932S401_REG_SRC_N_CTRL 0x10
|
| 45 | #define ICS932S401_REG_SRC_SPREAD1 0x11
|
| 46 | #define ICS932S401_REG_SRC_SPREAD2 0x12
|
| 47 | #define ICS932S401_REG_CPU_DIVISOR 0x13
|
| 48 | #define ICS932S401_CPU_DIVISOR_SHIFT 4
|
| 49 | #define ICS932S401_REG_PCISRC_DIVISOR 0x14
|
| 50 | #define ICS932S401_SRC_DIVISOR_MASK 0x0F
|
| 51 | #define ICS932S401_PCI_DIVISOR_SHIFT 4
|
| 52 |
|
| 53 |
|
| 54 | #define BASE_CLOCK 14318
|
| 55 |
|
| 56 | #define NUM_REGS 21
|
| 57 | #define NUM_MIRRORED_REGS 15
|
| 58 |
|
| 59 | static int regs_to_copy[NUM_MIRRORED_REGS] = {
|
| 60 | ICS932S401_REG_CFG2,
|
| 61 | ICS932S401_REG_CFG7,
|
| 62 | ICS932S401_REG_VENDOR_REV,
|
| 63 | ICS932S401_REG_DEVICE,
|
| 64 | ICS932S401_REG_CTRL,
|
| 65 | ICS932S401_REG_CPU_M_CTRL,
|
| 66 | ICS932S401_REG_CPU_N_CTRL,
|
| 67 | ICS932S401_REG_CPU_SPREAD1,
|
| 68 | ICS932S401_REG_CPU_SPREAD2,
|
| 69 | ICS932S401_REG_SRC_M_CTRL,
|
| 70 | ICS932S401_REG_SRC_N_CTRL,
|
| 71 | ICS932S401_REG_SRC_SPREAD1,
|
| 72 | ICS932S401_REG_SRC_SPREAD2,
|
| 73 | ICS932S401_REG_CPU_DIVISOR,
|
| 74 | ICS932S401_REG_PCISRC_DIVISOR,
|
| 75 | };
|
| 76 |
|
| 77 |
|
| 78 | #define SENSOR_REFRESH_INTERVAL (2 * HZ)
|
| 79 |
|
| 80 |
|
| 81 | #define LIMIT_REFRESH_INTERVAL (60 * HZ)
|
| 82 |
|
| 83 | struct ics932s401_data {
|
| 84 | struct attribute_group attrs;
|
| 85 | struct mutex lock;
|
| 86 | char sensors_valid;
|
| 87 | unsigned long sensors_last_updated;
|
| 88 |
|
| 89 | u8 regs[NUM_REGS];
|
| 90 | };
|
| 91 |
|
| 92 | static int ics932s401_probe(struct i2c_client *client);
|
| 93 | static int ics932s401_detect(struct i2c_client *client,
|
| 94 | struct i2c_board_info *info);
|
| 95 | static void ics932s401_remove(struct i2c_client *client);
|
| 96 |
|
| 97 | static const struct i2c_device_id ics932s401_id[] = {
|
| 98 | { .name = "ics932s401" },
|
| 99 | { }
|
| 100 | };
|
| 101 | MODULE_DEVICE_TABLE(i2c, ics932s401_id);
|
| 102 |
|
| 103 | static struct i2c_driver ics932s401_driver = {
|
| 104 | .class = I2C_CLASS_HWMON,
|
| 105 | .driver = {
|
| 106 | .name = "ics932s401",
|
| 107 | },
|
| 108 | .probe = ics932s401_probe,
|
| 109 | .remove = ics932s401_remove,
|
| 110 | .id_table = ics932s401_id,
|
| 111 | .detect = ics932s401_detect,
|
| 112 | .address_list = normal_i2c,
|
| 113 | };
|
| 114 |
|
| 115 | static struct ics932s401_data *ics932s401_update_device(struct device *dev)
|
| 116 | {
|
| 117 | struct i2c_client *client = to_i2c_client(dev);
|
| 118 | struct ics932s401_data *data = i2c_get_clientdata(client);
|
| 119 | unsigned long local_jiffies = jiffies;
|
| 120 | int i, temp;
|
| 121 |
|
| 122 | mutex_lock(&data->lock);
|
| 123 | if (time_before(local_jiffies, data->sensors_last_updated +
|
| 124 | SENSOR_REFRESH_INTERVAL)
|
| 125 | && data->sensors_valid)
|
| 126 | goto out;
|
| 127 |
|
| 128 | |
| 129 | * Each register must be read as a word and then right shifted 8 bits.
|
| 130 | * Not really sure why this is; setting the "byte count programming"
|
| 131 | * register to 1 does not fix this problem.
|
| 132 |
|
| 133 | for (i = 0; i < NUM_MIRRORED_REGS; i++) {
|
| 134 | temp = i2c_smbus_read_word_data(client, regs_to_copy[i]);
|
| 135 | if (temp < 0)
|
| 136 | temp = 0;
|
| 137 | data->regs[regs_to_copy[i]] = temp >> 8;
|
| 138 | }
|
| 139 |
|
| 140 | data->sensors_last_updated = local_jiffies;
|
| 141 | data->sensors_valid = 1;
|
| 142 |
|
| 143 | out:
|
| 144 | mutex_unlock(&data->lock);
|
| 145 | return data;
|
| 146 | }
|
| 147 |
|
| 148 | static ssize_t show_spread_enabled(struct device *dev,
|
| 149 | struct device_attribute *devattr,
|
| 150 | char *buf)
|
| 151 | {
|
| 152 | struct ics932s401_data *data = ics932s401_update_device(dev);
|
| 153 |
|
| 154 | if (data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD)
|
| 155 | return sprintf(buf, "1\n");
|
| 156 |
|
| 157 | return sprintf(buf, "0\n");
|
| 158 | }
|
| 159 |
|
| 160 |
|
| 161 | static const int fs_speeds[] = {
|
| 162 | 266666,
|
| 163 | 133333,
|
| 164 | 200000,
|
| 165 | 166666,
|
| 166 | 333333,
|
| 167 | 100000,
|
| 168 | 400000,
|
| 169 | 0,
|
| 170 | };
|
| 171 |
|
| 172 |
|
| 173 | static const int divisors[] = {2, 3, 5, 15, 4, 6, 10, 30, 8, 12, 20, 60, 16,
|
| 174 | 24, 40, 120};
|
| 175 |
|
| 176 |
|
| 177 | static int calculate_cpu_freq(struct ics932s401_data *data)
|
| 178 | {
|
| 179 | int m, n, freq;
|
| 180 |
|
| 181 | m = data->regs[ICS932S401_REG_CPU_M_CTRL] & ICS932S401_M_MASK;
|
| 182 | n = data->regs[ICS932S401_REG_CPU_N_CTRL];
|
| 183 |
|
| 184 |
|
| 185 | n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x80) << 1;
|
| 186 | n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x40) << 3;
|
| 187 |
|
| 188 | freq = BASE_CLOCK * (n + 8) / (m + 2);
|
| 189 | freq /= divisors[data->regs[ICS932S401_REG_CPU_DIVISOR] >>
|
| 190 | ICS932S401_CPU_DIVISOR_SHIFT];
|
| 191 |
|
| 192 | return freq;
|
| 193 | }
|
| 194 |
|
| 195 | static ssize_t show_cpu_clock(struct device *dev,
|
| 196 | struct device_attribute *devattr,
|
| 197 | char *buf)
|
| 198 | {
|
| 199 | struct ics932s401_data *data = ics932s401_update_device(dev);
|
| 200 |
|
| 201 | return sprintf(buf, "%d\n", calculate_cpu_freq(data));
|
| 202 | }
|
| 203 |
|
| 204 | static ssize_t show_cpu_clock_sel(struct device *dev,
|
| 205 | struct device_attribute *devattr,
|
| 206 | char *buf)
|
| 207 | {
|
| 208 | struct ics932s401_data *data = ics932s401_update_device(dev);
|
| 209 | int freq;
|
| 210 |
|
| 211 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED)
|
| 212 | freq = calculate_cpu_freq(data);
|
| 213 | else {
|
| 214 |
|
| 215 | int fid = data->regs[ICS932S401_REG_CFG7] & ICS932S401_FS_MASK;
|
| 216 |
|
| 217 | freq = fs_speeds[fid];
|
| 218 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT) {
|
| 219 | switch (freq) {
|
| 220 | case 166666:
|
| 221 | freq = 160000;
|
| 222 | break;
|
| 223 | case 333333:
|
| 224 | freq = 320000;
|
| 225 | break;
|
| 226 | }
|
| 227 | }
|
| 228 | }
|
| 229 |
|
| 230 | return sprintf(buf, "%d\n", freq);
|
| 231 | }
|
| 232 |
|
| 233 |
|
| 234 | static int calculate_src_freq(struct ics932s401_data *data)
|
| 235 | {
|
| 236 | int m, n, freq;
|
| 237 |
|
| 238 | m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK;
|
| 239 | n = data->regs[ICS932S401_REG_SRC_N_CTRL];
|
| 240 |
|
| 241 |
|
| 242 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1;
|
| 243 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3;
|
| 244 |
|
| 245 | freq = BASE_CLOCK * (n + 8) / (m + 2);
|
| 246 | freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] &
|
| 247 | ICS932S401_SRC_DIVISOR_MASK];
|
| 248 |
|
| 249 | return freq;
|
| 250 | }
|
| 251 |
|
| 252 | static ssize_t show_src_clock(struct device *dev,
|
| 253 | struct device_attribute *devattr,
|
| 254 | char *buf)
|
| 255 | {
|
| 256 | struct ics932s401_data *data = ics932s401_update_device(dev);
|
| 257 |
|
| 258 | return sprintf(buf, "%d\n", calculate_src_freq(data));
|
| 259 | }
|
| 260 |
|
| 261 | static ssize_t show_src_clock_sel(struct device *dev,
|
| 262 | struct device_attribute *devattr,
|
| 263 | char *buf)
|
| 264 | {
|
| 265 | struct ics932s401_data *data = ics932s401_update_device(dev);
|
| 266 | int freq;
|
| 267 |
|
| 268 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED)
|
| 269 | freq = calculate_src_freq(data);
|
| 270 | else
|
| 271 |
|
| 272 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT &&
|
| 273 | data->regs[ICS932S401_REG_CTRL] & ICS932S401_SRC_ALT)
|
| 274 | freq = 96000;
|
| 275 | else
|
| 276 | freq = 100000;
|
| 277 |
|
| 278 | return sprintf(buf, "%d\n", freq);
|
| 279 | }
|
| 280 |
|
| 281 |
|
| 282 | static int calculate_pci_freq(struct ics932s401_data *data)
|
| 283 | {
|
| 284 | int m, n, freq;
|
| 285 |
|
| 286 | m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK;
|
| 287 | n = data->regs[ICS932S401_REG_SRC_N_CTRL];
|
| 288 |
|
| 289 |
|
| 290 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1;
|
| 291 | n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3;
|
| 292 |
|
| 293 | freq = BASE_CLOCK * (n + 8) / (m + 2);
|
| 294 | freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] >>
|
| 295 | ICS932S401_PCI_DIVISOR_SHIFT];
|
| 296 |
|
| 297 | return freq;
|
| 298 | }
|
| 299 |
|
| 300 | static ssize_t show_pci_clock(struct device *dev,
|
| 301 | struct device_attribute *devattr,
|
| 302 | char *buf)
|
| 303 | {
|
| 304 | struct ics932s401_data *data = ics932s401_update_device(dev);
|
| 305 |
|
| 306 | return sprintf(buf, "%d\n", calculate_pci_freq(data));
|
| 307 | }
|
| 308 |
|
| 309 | static ssize_t show_pci_clock_sel(struct device *dev,
|
| 310 | struct device_attribute *devattr,
|
| 311 | char *buf)
|
| 312 | {
|
| 313 | struct ics932s401_data *data = ics932s401_update_device(dev);
|
| 314 | int freq;
|
| 315 |
|
| 316 | if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED)
|
| 317 | freq = calculate_pci_freq(data);
|
| 318 | else
|
| 319 | freq = 33333;
|
| 320 |
|
| 321 | return sprintf(buf, "%d\n", freq);
|
| 322 | }
|
| 323 |
|
| 324 | static ssize_t show_value(struct device *dev,
|
| 325 | struct device_attribute *devattr,
|
| 326 | char *buf);
|
| 327 |
|
| 328 | static ssize_t show_spread(struct device *dev,
|
| 329 | struct device_attribute *devattr,
|
| 330 | char *buf);
|
| 331 |
|
| 332 | static DEVICE_ATTR(spread_enabled, S_IRUGO, show_spread_enabled, NULL);
|
| 333 | static DEVICE_ATTR(cpu_clock_selection, S_IRUGO, show_cpu_clock_sel, NULL);
|
| 334 | static DEVICE_ATTR(cpu_clock, S_IRUGO, show_cpu_clock, NULL);
|
| 335 | static DEVICE_ATTR(src_clock_selection, S_IRUGO, show_src_clock_sel, NULL);
|
| 336 | static DEVICE_ATTR(src_clock, S_IRUGO, show_src_clock, NULL);
|
| 337 | static DEVICE_ATTR(pci_clock_selection, S_IRUGO, show_pci_clock_sel, NULL);
|
| 338 | static DEVICE_ATTR(pci_clock, S_IRUGO, show_pci_clock, NULL);
|
| 339 | static DEVICE_ATTR(usb_clock, S_IRUGO, show_value, NULL);
|
| 340 | static DEVICE_ATTR(ref_clock, S_IRUGO, show_value, NULL);
|
| 341 | static DEVICE_ATTR(cpu_spread, S_IRUGO, show_spread, NULL);
|
| 342 | static DEVICE_ATTR(src_spread, S_IRUGO, show_spread, NULL);
|
| 343 |
|
| 344 | static struct attribute *ics932s401_attr[] = {
|
| 345 | &dev_attr_spread_enabled.attr,
|
| 346 | &dev_attr_cpu_clock_selection.attr,
|
| 347 | &dev_attr_cpu_clock.attr,
|
| 348 | &dev_attr_src_clock_selection.attr,
|
| 349 | &dev_attr_src_clock.attr,
|
| 350 | &dev_attr_pci_clock_selection.attr,
|
| 351 | &dev_attr_pci_clock.attr,
|
| 352 | &dev_attr_usb_clock.attr,
|
| 353 | &dev_attr_ref_clock.attr,
|
| 354 | &dev_attr_cpu_spread.attr,
|
| 355 | &dev_attr_src_spread.attr,
|
| 356 | NULL
|
| 357 | };
|
| 358 |
|
| 359 | static ssize_t show_value(struct device *dev,
|
| 360 | struct device_attribute *devattr,
|
| 361 | char *buf)
|
| 362 | {
|
| 363 | int x;
|
| 364 |
|
| 365 | if (devattr == &dev_attr_usb_clock)
|
| 366 | x = 48000;
|
| 367 | else if (devattr == &dev_attr_ref_clock)
|
| 368 | x = BASE_CLOCK;
|
| 369 | else
|
| 370 | BUG();
|
| 371 |
|
| 372 | return sprintf(buf, "%d\n", x);
|
| 373 | }
|
| 374 |
|
| 375 | static ssize_t show_spread(struct device *dev,
|
| 376 | struct device_attribute *devattr,
|
| 377 | char *buf)
|
| 378 | {
|
| 379 | struct ics932s401_data *data = ics932s401_update_device(dev);
|
| 380 | int reg;
|
| 381 | unsigned long val;
|
| 382 |
|
| 383 | if (!(data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD))
|
| 384 | return sprintf(buf, "0%%\n");
|
| 385 |
|
| 386 | if (devattr == &dev_attr_src_spread)
|
| 387 | reg = ICS932S401_REG_SRC_SPREAD1;
|
| 388 | else if (devattr == &dev_attr_cpu_spread)
|
| 389 | reg = ICS932S401_REG_CPU_SPREAD1;
|
| 390 | else
|
| 391 | BUG();
|
| 392 |
|
| 393 | val = data->regs[reg] | (data->regs[reg + 1] << 8);
|
| 394 | val &= ICS932S401_SPREAD_MASK;
|
| 395 |
|
| 396 |
|
| 397 | val = 500000 * val / 16384;
|
| 398 | return sprintf(buf, "-0.%lu%%\n", val);
|
| 399 | }
|
| 400 |
|
| 401 |
|
| 402 | static int ics932s401_detect(struct i2c_client *client,
|
| 403 | struct i2c_board_info *info)
|
| 404 | {
|
| 405 | struct i2c_adapter *adapter = client->adapter;
|
| 406 | int vendor, device, revision;
|
| 407 |
|
| 408 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
| 409 | return -ENODEV;
|
| 410 |
|
| 411 | vendor = i2c_smbus_read_word_data(client, ICS932S401_REG_VENDOR_REV);
|
| 412 | vendor >>= 8;
|
| 413 | revision = vendor >> ICS932S401_REV_SHIFT;
|
| 414 | vendor &= ICS932S401_VENDOR_MASK;
|
| 415 | if (vendor != ICS932S401_VENDOR)
|
| 416 | return -ENODEV;
|
| 417 |
|
| 418 | device = i2c_smbus_read_word_data(client, ICS932S401_REG_DEVICE);
|
| 419 | device >>= 8;
|
| 420 | if (device != ICS932S401_DEVICE)
|
| 421 | return -ENODEV;
|
| 422 |
|
| 423 | if (revision != ICS932S401_REV)
|
| 424 | dev_info(&adapter->dev, "Unknown revision %d\n", revision);
|
| 425 |
|
| 426 | strscpy(info->type, "ics932s401", I2C_NAME_SIZE);
|
| 427 |
|
| 428 | return 0;
|
| 429 | }
|
| 430 |
|
| 431 | static int ics932s401_probe(struct i2c_client *client)
|
| 432 | {
|
| 433 | struct ics932s401_data *data;
|
| 434 | int err;
|
| 435 |
|
| 436 | data = kzalloc_obj(struct ics932s401_data);
|
| 437 | if (!data) {
|
| 438 | err = -ENOMEM;
|
| 439 | goto exit;
|
| 440 | }
|
| 441 |
|
| 442 | i2c_set_clientdata(client, data);
|
| 443 | mutex_init(&data->lock);
|
| 444 |
|
| 445 | dev_info(&client->dev, "%s chip found\n", client->name);
|
| 446 |
|
| 447 |
|
| 448 | data->attrs.attrs = ics932s401_attr;
|
| 449 | err = sysfs_create_group(&client->dev.kobj, &data->attrs);
|
| 450 | if (err)
|
| 451 | goto exit_free;
|
| 452 |
|
| 453 | return 0;
|
| 454 |
|
| 455 | exit_free:
|
| 456 | kfree(data);
|
| 457 | exit:
|
| 458 | return err;
|
| 459 | }
|
| 460 |
|
| 461 | static void ics932s401_remove(struct i2c_client *client)
|
| 462 | {
|
| 463 | struct ics932s401_data *data = i2c_get_clientdata(client);
|
| 464 |
|
| 465 | sysfs_remove_group(&client->dev.kobj, &data->attrs);
|
| 466 | kfree(data);
|
| 467 | }
|
| 468 |
|
| 469 | module_i2c_driver(ics932s401_driver);
|
| 470 |
|
| 471 | MODULE_AUTHOR("Darrick J. Wong <[email protected]>");
|
| 472 | MODULE_DESCRIPTION("ICS932S401 driver");
|
| 473 | MODULE_LICENSE("GPL");
|
| 474 |
|
| 475 |
|
| 476 | MODULE_ALIAS("dmi:bvnIBM:*:rn9228:*");
|
| 477 | MODULE_ALIAS("dmi:bvnIBM:*:rn9232:*");
|
| 478 |
|
| 479 |
|
| 480 | MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650*");
|
| 481 | MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550*");
|