Commit a412ae3fb90ab49072b82c8cfa1e3e60d2b27005

Authored by Darrick J. Wong
Committed by Linus Torvalds
1 parent c0b4e3ab0c

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>

Showing 4 changed files with 557 additions and 0 deletions Side-by-side Diff

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!
drivers/misc/Kconfig
... ... @@ -227,6 +227,16 @@
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
drivers/misc/Makefile
... ... @@ -14,6 +14,7 @@
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
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*");