bk://kernel.bkbits.net/gregkh/linux/i2c-2.6 rafael.espindola@gmail.com[gregkh]|ChangeSet|20050324001204|34949 rafael.espindola # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2005/03/23 19:37:35-08:00 akpm@bix.(none) # Merge bk://kernel.bkbits.net/gregkh/linux/i2c-2.6 # into bix.(none):/usr/src/bk-i2c # # drivers/i2c/chips/eeprom.c # 2005/03/23 19:37:31-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/03/23 16:12:04-08:00 rafael.espindola@gmail.com # [PATCH] I2C: lsb in emc6d102 and adm1027 # # The attached patches add support for reading the lsb from the emc6d102 # and change how they are read from the adm1027. # # The lm85_update_device function decodes the LSBs to temp_ext and in_ext. # This strategy was suggested by Philip Pokorny. # # The patch also changes some macros to use the SCALE macro. I think that # they become more readable this way. # # # Signed-off-by: Rafael Ávila de Espíndola # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/lm85.c # 2005/03/23 16:00:00-08:00 rafael.espindola@gmail.com +77 -23 # I2C: lsb in emc6d102 and adm1027 # # ChangeSet # 2005/03/23 16:11:37-08:00 ebrower@gmail.com # [PATCH] I2C: lost arbitration detection for PCF8584 # # [PATCH] lost arbitration detection for PCF8584 algo driver # # Patch against a slightly-dated linux-2.6 BK tree # # This patch provides lost arbitration detection for the PCF8584 # I2C algorithm driver. The PCF8584 LAB bit is set whenever lost # arbitration is detected, so we check the bit in the wait_for_pin # function and if LAB is detected we return -EINTR. The -EINTR # value bubbles-up all the way to the master_xfer API call so # callers may detect this condition explicitly. LAB could be checked # more often, at the expense of code readability/maintainability. # # Signed-off-by: Eric Brower # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/algos/i2c-algo-pcf.c # 2005/02/11 17:50:20-08:00 ebrower@gmail.com +41 -3 # I2C: lost arbitration detection for PCF8584 # # ChangeSet # 2005/03/23 16:10:58-08:00 R.Marek@sh.cvut.cz # [PATCH] I2C: busses documentation update 2 of 2 # # Patch contains promised documentation update for i2c bus drivers. # I would like to thank Jean Delvare and Aurelien Jarno for their # comments. # # Signed-off-by: Rudolf Marek # Signed-off-by: Greg Kroah-Hartman # # Documentation/i2c/busses/scx200_acb # 2005/02/17 15:29:54-08:00 R.Marek@sh.cvut.cz +14 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-voodoo3 # 2005/02/24 08:22:14-08:00 R.Marek@sh.cvut.cz +62 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-via # 2005/02/24 08:21:17-08:00 R.Marek@sh.cvut.cz +34 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-viapro # 2005/02/24 08:21:27-08:00 R.Marek@sh.cvut.cz +47 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-sis69x # 2005/03/04 11:22:32-08:00 R.Marek@sh.cvut.cz +73 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-sis630 # 2005/03/04 11:20:53-08:00 R.Marek@sh.cvut.cz +49 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-sis5595 # 2005/03/04 11:18:39-08:00 R.Marek@sh.cvut.cz +59 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-savage4 # 2005/02/24 08:19:09-08:00 R.Marek@sh.cvut.cz +26 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-prosavage # 2005/02/24 08:19:01-08:00 R.Marek@sh.cvut.cz +23 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-piix4 # 2005/03/04 11:18:10-08:00 R.Marek@sh.cvut.cz +72 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-pca-isa # 2005/02/17 15:29:53-08:00 R.Marek@sh.cvut.cz +23 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-parport-light # 2005/03/04 11:23:31-08:00 R.Marek@sh.cvut.cz +11 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-nforce2 # 2005/02/24 08:17:48-08:00 R.Marek@sh.cvut.cz +41 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-i810 # 2005/02/24 08:14:29-08:00 R.Marek@sh.cvut.cz +46 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-i801 # 2005/02/24 08:13:42-08:00 R.Marek@sh.cvut.cz +80 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-amd8111 # 2005/02/24 08:13:13-08:00 R.Marek@sh.cvut.cz +41 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-amd756 # 2005/02/24 08:12:32-08:00 R.Marek@sh.cvut.cz +25 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-ali15x3 # 2005/03/04 11:14:39-08:00 R.Marek@sh.cvut.cz +112 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-ali1563 # 2005/03/04 11:16:10-08:00 R.Marek@sh.cvut.cz +27 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-ali1535 # 2005/02/24 08:07:40-08:00 R.Marek@sh.cvut.cz +42 -0 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/scx200_acb # 2005/02/17 15:29:54-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/scx200_acb # # Documentation/i2c/busses/i2c-voodoo3 # 2005/02/24 08:22:14-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-voodoo3 # # Documentation/i2c/busses/i2c-via # 2005/02/24 08:21:17-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-via # # Documentation/i2c/busses/i2c-viapro # 2005/02/24 08:21:27-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-viapro # # Documentation/i2c/busses/i2c-sis69x # 2005/03/04 11:22:32-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-sis69x # # Documentation/i2c/busses/i2c-sis630 # 2005/03/04 11:20:53-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-sis630 # # Documentation/i2c/busses/i2c-sis5595 # 2005/03/04 11:18:39-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-sis5595 # # Documentation/i2c/busses/i2c-savage4 # 2005/02/24 08:19:09-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-savage4 # # Documentation/i2c/busses/i2c-prosavage # 2005/02/24 08:19:01-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-prosavage # # Documentation/i2c/busses/i2c-piix4 # 2005/03/04 11:18:10-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-piix4 # # Documentation/i2c/busses/i2c-pca-isa # 2005/02/17 15:29:53-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-pca-isa # # Documentation/i2c/busses/i2c-parport # 2005/02/24 08:18:08-08:00 R.Marek@sh.cvut.cz +8 -10 # I2C: busses documentation update 2 of 2 # # Documentation/i2c/busses/i2c-parport-light # 2005/03/04 11:23:31-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-parport-light # # Documentation/i2c/busses/i2c-nforce2 # 2005/02/24 08:17:48-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-nforce2 # # Documentation/i2c/busses/i2c-i810 # 2005/02/24 08:14:29-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-i810 # # Documentation/i2c/busses/i2c-i801 # 2005/02/24 08:13:42-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-i801 # # Documentation/i2c/busses/i2c-amd8111 # 2005/02/24 08:13:13-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-amd8111 # # Documentation/i2c/busses/i2c-amd756 # 2005/02/24 08:12:32-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-amd756 # # Documentation/i2c/busses/i2c-ali15x3 # 2005/03/04 11:14:39-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-ali15x3 # # Documentation/i2c/busses/i2c-ali1563 # 2005/03/04 11:16:10-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-ali1563 # # Documentation/i2c/busses/i2c-ali1535 # 2005/02/24 08:07:40-08:00 R.Marek@sh.cvut.cz +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/Documentation/i2c/busses/i2c-ali1535 # # ChangeSet # 2005/03/23 16:10:36-08:00 R.Marek@sh.cvut.cz # [PATCH] I2C: busses documentation update 1 of 2 # # This patch just moves i2c-parport file to busses directory. # Patch for other busses documentation will follow. # # Signed-off-by: Rudolf Marek # Signed-off-by: Greg Kroah-Hartman # # Documentation/i2c/busses/i2c-parport # 2005/02/17 15:29:53-08:00 R.Marek@sh.cvut.cz +0 -0 # I2C: busses documentation update 1 of 2 # # ChangeSet # 2005/03/23 15:55:45-08:00 khali@linux-fr.org # [PATCH] I2C: Fix Vaio EEPROM detection # # This fixes a bug in the eeprom driver, which made all EEPROMs at # location 0x57 be erroneously treated as Vaio EEPROMs. I have to say I'm # quite ashamed that I introduced the bug in the first place, as this was # a really stupid one. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/eeprom.c # 2005/03/17 06:33:29-08:00 khali@linux-fr.org +2 -1 # I2C: Fix Vaio EEPROM detection # # ChangeSet # 2005/03/23 15:55:25-08:00 khali@linux-fr.org # [PATCH] I2C: Recognize new revision of the ADT7463 chip # # This simple patch to the lm85 driver adds recognition of a new revision # of the ADT7463 chip. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/lm85.c # 2005/03/18 14:03:42-08:00 khali@linux-fr.org +3 -1 # I2C: Recognize new revision of the ADT7463 chip # # ChangeSet # 2005/03/23 15:55:04-08:00 khali@linux-fr.org # [PATCH] I2C: Avoid repeated resets of i2c-viapro # # It was reported that the i2c-viapro SMBus driver sometimes has trouble # on recent systems (VT8237 south bridge). The "Host Status" register has # at least one additional bit used when compared with older south bridges # of this family. The driver currently considers this additional bit as an # error condition when it's set, causing repeated bus resets and sometimes # read failures. # # This patch makes the driver ignore the bits of the "Host Status" # register for which no definition is known. I wish I had a datasheet for # the VIA VT8237, so that I could check what the additional bit is # supposed to mean, but I don't. If someone has a datasheet or good # contacts at VIA, please let me know. # # The patch was reported to fix the problem on a system with the VT8237, # and was also tested not to break the driver on older VIA south bridges, # so it seems to be safe. Thanks to Aurelien Jarno for the tests. # # Additionally, the patch makes the post-transaction bus reset slightly # more efficient by sparing a few unneeded I/O operations. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/busses/i2c-viapro.c # 2005/03/13 11:05:46-08:00 khali@linux-fr.org +9 -8 # I2C: Avoid repeated resets of i2c-viapro # # ChangeSet # 2005/03/23 15:54:45-08:00 khali@linux-fr.org # [PATCH] I2C: Kill outdated defines in i2c.h # # Some defines in i2c.h (I2C_CLIENT_MODPARM and friends) are now useless. # They should have been removed when the i2c client parameters were # converted from MODULE_PARAM to module_parm_array, but where not. This # patch removes them now. # # Additionally, it moves the definition of I2C_CLIENT_MAX_OPTS next to # where it is used rather than 220 lines before, which is preferable IMHO. # # As a side note, I think that there is a bug in the way these options are # handled. The i2c code looks for I2C_CLIENT_END as a list terminator, but # if the maximum number of parameters are actually provided, no terminator # will be left. It's rather unlikely to happen because nobody will # probably ever provide that many parameters, but this should probably be # fixed. I'll address this issue later, since I plan to completely rewrite # the way these parameters are handled anyway. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # include/linux/i2c.h # 2005/03/20 03:26:34-08:00 khali@linux-fr.org +3 -10 # I2C: Kill outdated defines in i2c.h # # ChangeSet # 2005/03/23 15:54:02-08:00 khali@linux-fr.org # [PATCH] I2C: Fix some i2c algorithm initialization # # While searching for i2c_algorithm declarations missing their # .functionality member, I found three of them which were not properly # initialized. i2c-algo-ite and i2c_sibyte_algo do not use the C99 # initialization syntax, and i2c-ibm_iic.c explicitely initializes NULL # members. Following patch puts some order in there. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/busses/i2c-ibm_iic.c # 2005/03/23 07:59:21-08:00 khali@linux-fr.org +0 -4 # I2C: Fix some i2c algorithm initialization # # drivers/i2c/algos/i2c-algo-sibyte.c # 2005/03/23 08:07:04-08:00 khali@linux-fr.org +5 -8 # I2C: Fix some i2c algorithm initialization # # drivers/i2c/algos/i2c-algo-ite.c # 2005/03/23 08:09:51-08:00 khali@linux-fr.org +5 -8 # I2C: Fix some i2c algorithm initialization # # ChangeSet # 2005/03/16 22:58:37-08:00 mgreer@mvista.com # [PATCH] I2C: Fix breakage in m41t00 i2c rtc driver # # Remove setting of deleted i2c_client structure member. # # The latest include/linux/i2c.h:i2c_client structure no longer has an # 'id' member. This patch removes the setting of that no longer existing # member. # # Signed-off-by: Mark A. Greer # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/m41t00.c # 2005/03/08 16:18:24-08:00 mgreer@mvista.com +0 -1 # I2C: Fix breakage in m41t00 i2c rtc driver # # ChangeSet # 2005/03/16 22:58:09-08:00 frank.beesley@aeroflex.com # [PATCH] I2C: Clean up of i2c-elektor.c build # # This patch changes the flags variable type from long to unsigned long in # one function. This removes a couple of warnings from the compile # messages for elektor i2c bus driver. # # Signed-off-by: Frank Beesley # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/busses/i2c-elektor.c # 2005/03/11 12:03:44-08:00 frank.beesley@aeroflex.com +1 -1 # I2C: Clean up of i2c-elektor.c build # # ChangeSet # 2005/03/16 22:54:03-08:00 jchapman@katalix.com # [PATCH] i2c: add adt7461 chip support to lm90 driver # # i2c: add adt7461 chip support # # The Analog Devices ADT7461 temperature sensor chip is compatible with # the lm90 device provided its extended temperature range is not # enabled. The chip will be ignored if the boot firmware enables # extended temperature range. # # Also, since the adt7461 treats temp values <0 as 0 and >127 as 127, # the driver prevents temperature values outside the supported range # from being set. # # Signed-off-by: James Chapman # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/lm90.c # 2005/03/16 22:51:41-08:00 jchapman@katalix.com +41 -3 # i2c: add adt7461 chip support to lm90 driver # # ChangeSet # 2005/03/16 22:49:47-08:00 jchapman@katalix.com # [PATCH] i2c: new driver for ds1337 RTC # # Signed-off-by: James Chapman # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/ds1337.c # 2005/03/07 02:33:52-08:00 jchapman@katalix.com +402 -0 # i2c: new driver for ds1337 RTC # # drivers/i2c/chips/Makefile # 2005/03/04 08:59:53-08:00 jchapman@katalix.com +1 -0 # i2c: new driver for ds1337 RTC # # drivers/i2c/chips/Kconfig # 2005/03/04 08:59:53-08:00 jchapman@katalix.com +11 -0 # i2c: new driver for ds1337 RTC # # drivers/i2c/chips/ds1337.c # 2005/03/07 02:33:52-08:00 jchapman@katalix.com +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/i2c/chips/ds1337.c # # ChangeSet # 2005/03/16 22:39:26-08:00 grant_nospam@dodo.com.au # [PATCH] I2C: group Intel on I2C Hardware Bus support # # From an end-user perspective it is easy to miss the third Intel PIIX # entry on the menuconfig "I2C Hardware Bus support" screen. # Also the Intel 801 menu item does not mention ICH. # # This trivial patch groups three Intel entries together, adds ICH to # menu item, and ICH5/ICH5R to the help section. Includes suggestions # from Jean Delvare. # # Signed-off-by: Grant Coady # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/busses/Kconfig # 2005/03/16 14:45:14-08:00 grant_nospam@dodo.com.au +19 -19 # I2C: group Intel on I2C Hardware Bus support # # ChangeSet # 2005/03/16 22:39:00-08:00 khali@linux-fr.org # [PATCH] I2C: Skip broken detection step in it87 # # One of the detection steps in the it87 chip driver was reported to be # broken for some revisions of the IT8712F chip [1] [2]. This detection # step is a legacy from the lm78 driver and the documentation available # for the IT8705F and IT8712F chips does not mention it at all. For this # reason, I propose to skip this detection step for Super-I/O chips. # Super-I/O chips have already been identified when we reach this step, so # it is redundant (additionally do being broken). This closes bug #4335. # # [1] http://bugzilla.kernel.org/show_bug.cgi?id=4335 # [2] http://archives.andrew.net.au/lm-sensors/msg29962.html # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/it87.c # 2005/03/15 11:51:14-08:00 khali@linux-fr.org +2 -3 # I2C: Skip broken detection step in it87 # # ChangeSet # 2005/03/16 22:38:32-08:00 khali@linux-fr.org # [PATCH] I2C: Make master_xfer debug messages more useful # # While working on the recent saa7110 mess, I found that the debug message # displayed when calling master_xfer wasn't as useful as it could be. Here # is a patch improving this. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/i2c-core.c # 2005/03/08 10:06:36-08:00 khali@linux-fr.org +7 -1 # I2C: Make master_xfer debug messages more useful # # ChangeSet # 2005/03/16 22:38:06-08:00 khali@linux-fr.org # [PATCH] I2C: Kill unused struct members in w83627hf driver # # I just noticed that the pwmenable struct members in the w83627hf driver # are not used anywhere (and quite rightly so, as PWM cannot be disabled # in these chips as far as I know). Let's just get rid of them and save # some bytes of memory. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/w83627hf.c # 2005/03/07 02:56:22-08:00 khali@linux-fr.org +0 -5 # I2C: Kill unused struct members in w83627hf driver # # ChangeSet # 2005/03/16 22:37:40-08:00 khali@linux-fr.org # [PATCH] I2C: Fix adm1021 alarms mask # # This patch fixes an incorrect bitmasking on the status register in the # adm1021 driver, which was causing high alarm on remote temperature to be # hidden. # This bug was found and reported by Jayakrishnan: # http://bugzilla.kernel.org/show_bug.cgi?id=4285 # # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/adm1021.c # 2005/03/04 05:03:33-08:00 khali@linux-fr.org +1 -1 # I2C: Fix adm1021 alarms mask # # ChangeSet # 2005/03/16 22:37:17-08:00 khali@linux-fr.org # [PATCH] I2C: Cleanup adm1021 unused defines # # While working on the adm1021 driver, I found that this driver has a # number of unused (and useless) defines we could get rid of. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/adm1021.c # 2005/03/04 05:24:16-08:00 khali@linux-fr.org +0 -12 # I2C: Cleanup adm1021 unused defines # # ChangeSet # 2005/03/16 22:36:58-08:00 khali@linux-fr.org # [PATCH] I2C: New lm92 chip driver # # This is a new i2c chip driver named lm92. It supports the National # Semiconductor LM92 and Maxim MAX6635 chips. This time I did not port the # driver from the lm_sensors project but instead rewrote it. The reason is # that the original driver has a different structure from the other i2c # chip drivers, which would have made maintenance harder. # # I don't have a compatible chip myself but could test my code thanks to # Mark Hoffman's i2c-stub fake bus driver. Later on, James Chapman tested # it on a real chip, successfully. # # Signed-off-by: Jean Delvare # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/chips/lm92.c # 2005/03/06 00:19:47-08:00 khali@linux-fr.org +423 -0 # I2C: New lm92 chip driver # # drivers/i2c/chips/lm92.c # 2005/03/06 00:19:47-08:00 khali@linux-fr.org +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/i2c/chips/lm92.c # # drivers/i2c/chips/Makefile # 2005/03/06 00:19:47-08:00 khali@linux-fr.org +1 -0 # I2C: New lm92 chip driver # # drivers/i2c/chips/Kconfig # 2005/03/06 00:19:47-08:00 khali@linux-fr.org +11 -0 # I2C: New lm92 chip driver # # ChangeSet # 2005/03/16 22:36:33-08:00 domen@coderock.org # [PATCH] i2c/i2c-elektor: remove interruptible_sleep_on_timeout() usage # # Replace deprecated interruptible_sleep_on_timeout() with direct # wait-queue usage. Patch is compile-tested. # # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Domen Puncer # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/busses/i2c-elektor.c # 2005/03/05 07:12:06-08:00 domen@coderock.org +4 -3 # i2c/i2c-elektor: remove interruptible_sleep_on_timeout() usage # # ChangeSet # 2005/03/16 22:36:08-08:00 domen@coderock.org # [PATCH] i2c/i2c-ite: remove interruptible_sleep_on_timeout() usage # # Replace deprecated interruptible_sleep_on_timeout() with direct # wait-queue usage. Patch is compile-tested, sort of; the driver does not build in # vanilla kernel either, but I don't seem to add any warnings.. # # Signed-off-by: Nishanth Aravamudan # Signed-off-by: Domen Puncer # Signed-off-by: Greg Kroah-Hartman # # drivers/i2c/busses/i2c-ite.c # 2005/03/05 07:12:07-08:00 domen@coderock.org +5 -2 # i2c/i2c-ite: remove interruptible_sleep_on_timeout() usage # # ChangeSet # 2005/03/14 21:31:27-08:00 johnpol@2ka.mipt.ru # [PATCH] superio: get rid of the potential problems with atomic operations. # # Get rid of the potential problems with atomic operations. # # According to upcoming atomic_ops.txt by David Miller and Anton Blanchard # some archs may reoder atomic operations with nonatomic, since # the former are always visible but the latter are not, this can lead # to unpredicted behaviour. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # drivers/superio/sc.c # 2005/03/02 09:56:38-08:00 johnpol@2ka.mipt.ru +24 -2 # superio: get rid of the potential problems with atomic operations. # # ChangeSet # 2005/03/14 21:31:01-08:00 johnpol@2ka.mipt.ru # [PATCH] kobject_uevent: prefill allocated buffer with 0 before use. # # Prefill allocated buffer with 0 before use. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # lib/kobject_uevent.c # 2005/03/02 09:56:56-08:00 johnpol@2ka.mipt.ru +2 -0 # kobject_uevent: prefill allocated buffer with 0 before use. # # ChangeSet # 2005/03/14 21:30:28-08:00 johnpol@2ka.mipt.ru # [PATCH] connector: fix skb leak on big messages. # # If message size exceeds CONNECTOR_MAX_MSG_SIZE skb can be leaked. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # drivers/connector/connector.c # 2005/03/02 09:57:02-08:00 johnpol@2ka.mipt.ru +7 -2 # connector: fix skb leak on big messages. # # ChangeSet # 2005/03/14 21:29:59-08:00 johnpol@2ka.mipt.ru # [PATCH] connector: allow sent to self messages. # # Allow sending messages to itself. # Since netlink sending routing do not allow(will not allow) # shared skb, we will clone original skb, if it fails # skb will be delivered only to remote groups. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # drivers/connector/connector.c # 2005/03/02 09:57:09-08:00 johnpol@2ka.mipt.ru +9 -2 # connector: allow sent to self messages. # # ChangeSet # 2005/03/14 21:29:24-08:00 johnpol@2ka.mipt.ru # [PATCH] connector: get rid of the potential problems with atomic operations. # # Get rid of the potential problems with atomic operations. # # According to upcoming atomic_ops.txt by David Miller and Anton Blanchard # some archs may reoder atomic operations with nonatomic, since # the former are always visible but the latter are not, this can lead # to unpredicted behaviour. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # drivers/connector/cn_queue.c # 2005/03/02 09:57:18-08:00 johnpol@2ka.mipt.ru +14 -2 # connector: get rid of the potential problems with atomic operations. # # ChangeSet # 2005/03/14 21:28:54-08:00 johnpol@2ka.mipt.ru # [PATCH] connector: Use DEFINE_SPINLOCK. # # Use DEFINE_SPINLOCK for private connector's notify lock. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # drivers/connector/connector.c # 2005/03/02 09:58:14-08:00 johnpol@2ka.mipt.ru +1 -1 # connector: Use DEFINE_SPINLOCK. # # ChangeSet # 2005/03/14 21:28:26-08:00 johnpol@2ka.mipt.ru # [PATCH] superio: Use msleep* calls instead of direct schedule* calls. # # Use msleep* calls instead of direct schedule* calls. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # drivers/superio/scx.c # 2005/03/02 09:59:36-08:00 johnpol@2ka.mipt.ru +5 -5 # superio: Use msleep* calls instead of direct schedule* calls. # # drivers/superio/sc.c # 2005/03/02 09:59:36-08:00 johnpol@2ka.mipt.ru +10 -10 # superio: Use msleep* calls instead of direct schedule* calls. # # drivers/superio/pc8736x.c # 2005/03/02 09:59:36-08:00 johnpol@2ka.mipt.ru +5 -5 # superio: Use msleep* calls instead of direct schedule* calls. # # ChangeSet # 2005/03/14 21:27:55-08:00 johnpol@2ka.mipt.ru # [PATCH] superio: change scx200 module name to scx. # # Change scx200 module name to scx. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # drivers/superio/scx.h # 2005/03/02 09:59:42-08:00 johnpol@2ka.mipt.ru +1 -1 # superio: change scx200 module name to scx. # # drivers/superio/scx.c # 2005/03/02 09:59:42-08:00 johnpol@2ka.mipt.ru +2 -2 # superio: change scx200 module name to scx. # # drivers/superio/Makefile # 2005/03/02 09:59:42-08:00 johnpol@2ka.mipt.ru +1 -1 # superio: change scx200 module name to scx. # # ChangeSet # 2005/03/14 21:27:16-08:00 johnpol@2ka.mipt.ru # [PATCH] superio: remove unneded exports and make some functions static. # # Remove unneded exports and make some functions static. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # drivers/superio/sc_gpio.h # 2005/03/02 09:59:50-08:00 johnpol@2ka.mipt.ru +0 -7 # superio: remove unneded exports and make some functions static. # # drivers/superio/sc_gpio.c # 2005/03/02 09:59:50-08:00 johnpol@2ka.mipt.ru +16 -14 # superio: remove unneded exports and make some functions static. # # drivers/superio/sc_acb.c # 2005/03/02 09:59:50-08:00 johnpol@2ka.mipt.ru +10 -15 # superio: remove unneded exports and make some functions static. # # ChangeSet # 2005/03/14 21:26:44-08:00 johnpol@2ka.mipt.ru # [PATCH] Re: kobject_uevent.c moved to kernel connector. # # kobject_uevent.c change which allows to use new kernel connector # interface. More details at # http://marc.theaimsgroup.com/?l=linux-kernel&m=110370721906005&w=2 # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # lib/kobject_uevent.c # 2005/03/02 10:00:13-08:00 johnpol@2ka.mipt.ru +66 -0 # Re: kobject_uevent.c moved to kernel connector. # # ChangeSet # 2005/03/14 21:26:15-08:00 greg@kroah.com # [PATCH] superio: fix up some sparse warnings in the code # # Signed-off-by: Greg Kroah-Hartman # # drivers/superio/scx200.c # 2005/03/02 10:00:20-08:00 greg@kroah.com +5 -5 # superio: fix up some sparse warnings in the code # # drivers/superio/sc_gpio.c # 2005/03/02 10:00:20-08:00 greg@kroah.com +1 -1 # superio: fix up some sparse warnings in the code # # drivers/superio/pc8736x.c # 2005/03/02 10:00:20-08:00 greg@kroah.com +6 -6 # superio: fix up some sparse warnings in the code # # ChangeSet # 2005/03/14 21:25:41-08:00 johnpol@2ka.mipt.ru # [PATCH] superio: add SuperIO subsystem. # # SuperIO subsystem is new interface for different chips # that implement various logical devices inside. # Many embedded boards use such chips like SC1100 Geode processor # and PC87366. The latter for example contains # GPIO, Access Bus, Watchdog timer, Game port, MIDI port, # Voltage level monitor and Temperature sensor. # # SuperIO subsystem currently supports GPIO and # Access Bus logical devices in PC8736x and SCx200/SC1100 chips. # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # include/linux/sc_conn.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +50 -0 # superio: add SuperIO subsystem. # # drivers/superio/scx200.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +28 -0 # superio: add SuperIO subsystem. # # drivers/superio/scx200.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +413 -0 # superio: add SuperIO subsystem. # # drivers/superio/sc_w1.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +107 -0 # superio: add SuperIO subsystem. # # drivers/superio/sc_gpio.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +57 -0 # superio: add SuperIO subsystem. # # drivers/superio/sc_gpio.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +310 -0 # superio: add SuperIO subsystem. # # drivers/superio/sc_conn.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +124 -0 # superio: add SuperIO subsystem. # # drivers/superio/sc_acb.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +45 -0 # superio: add SuperIO subsystem. # # drivers/superio/sc_acb.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +163 -0 # superio: add SuperIO subsystem. # # drivers/superio/sc.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +134 -0 # superio: add SuperIO subsystem. # # drivers/superio/sc.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +780 -0 # superio: add SuperIO subsystem. # # drivers/superio/pin_test.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +93 -0 # superio: add SuperIO subsystem. # # include/linux/sc_conn.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/include/linux/sc_conn.h # # include/linux/connector.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +4 -1 # superio: add SuperIO subsystem. # # drivers/superio/scx200.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/scx200.h # # drivers/superio/scx200.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/scx200.c # # drivers/superio/sc_w1.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/sc_w1.c # # drivers/superio/sc_gpio.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/sc_gpio.h # # drivers/superio/sc_gpio.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/sc_gpio.c # # drivers/superio/sc_conn.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/sc_conn.c # # drivers/superio/sc_acb.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/sc_acb.h # # drivers/superio/sc_acb.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/sc_acb.c # # drivers/superio/sc.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/sc.h # # drivers/superio/sc.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/sc.c # # drivers/superio/pin_test.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/pin_test.c # # drivers/superio/pc8736x.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +39 -0 # superio: add SuperIO subsystem. # # drivers/superio/pc8736x.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +209 -0 # superio: add SuperIO subsystem. # # drivers/superio/chain.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +37 -0 # superio: add SuperIO subsystem. # # drivers/superio/chain.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +52 -0 # superio: add SuperIO subsystem. # # drivers/superio/Makefile # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +11 -0 # superio: add SuperIO subsystem. # # drivers/superio/Kconfig # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +56 -0 # superio: add SuperIO subsystem. # # drivers/Makefile # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +1 -0 # superio: add SuperIO subsystem. # # drivers/Kconfig # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +2 -0 # superio: add SuperIO subsystem. # # drivers/superio/pc8736x.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/pc8736x.h # # drivers/superio/pc8736x.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/pc8736x.c # # drivers/superio/chain.h # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/chain.h # # drivers/superio/chain.c # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/chain.c # # drivers/superio/Makefile # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/Makefile # # drivers/superio/Kconfig # 2005/03/02 10:00:28-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/superio/Kconfig # # ChangeSet # 2005/03/14 21:25:11-08:00 johnpol@2ka.mipt.ru # [PATCH] Add Kernel conector subsystem # # Signed-off-by: Evgeniy Polyakov # Signed-off-by: Greg Kroah-Hartman # # include/linux/connector.h # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +147 -0 # Add Kernel conector subsystem # # drivers/connector/connector.c # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +509 -0 # Add Kernel conector subsystem # # drivers/connector/cn_queue.c # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +219 -0 # Add Kernel conector subsystem # # drivers/connector/Makefile # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +2 -0 # Add Kernel conector subsystem # # include/linux/connector.h # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/include/linux/connector.h # # drivers/connector/connector.c # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/connector/connector.c # # drivers/connector/cn_queue.c # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/connector/cn_queue.c # # drivers/connector/Makefile # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/connector/Makefile # # drivers/connector/Kconfig # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +13 -0 # Add Kernel conector subsystem # # drivers/Makefile # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +2 -0 # Add Kernel conector subsystem # # drivers/Kconfig # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +2 -0 # Add Kernel conector subsystem # # drivers/connector/Kconfig # 2005/03/02 10:01:44-08:00 johnpol@2ka.mipt.ru +0 -0 # BitKeeper file /home/greg/linux/BK/i2c-2.6/drivers/connector/Kconfig # diff -Nru a/Documentation/i2c/busses/i2c-ali1535 b/Documentation/i2c/busses/i2c-ali1535 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-ali1535 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,42 @@ +Kernel driver i2c-ali1535 + +Supported adapters: + * Acer Labs, Inc. ALI 1535 (south bridge) + Datasheet: Now under NDA + http://www.ali.com.tw/eng/support/datasheet_request.php + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Mark D. Studebaker , + Dan Eaton , + Stephen Rousset + +Description +----------- + +This is the driver for the SMB Host controller on Acer Labs Inc. (ALI) +M1535 South Bridge. + +The M1535 is a South bridge for portable systems. It is very similar to the +M15x3 South bridges also produced by Acer Labs Inc. Some of the registers +within the part have moved and some have been redefined slightly. +Additionally, the sequencing of the SMBus transactions has been modified to +be more consistent with the sequencing recommended by the manufacturer and +observed through testing. These changes are reflected in this driver and +can be identified by comparing this driver to the i2c-ali15x3 driver. For +an overview of these chips see http://www.acerlabs.com + +The SMB controller is part of the M7101 device, which is an ACPI-compliant +Power Management Unit (PMU). + +The whole M7101 device has to be enabled for the SMB to work. You can't +just enable the SMB alone. The SMB and the ACPI have separate I/O spaces. +We make sure that the SMB is enabled. We leave the ACPI alone. + + +Features +-------- + +This driver controls the SMB Host only. This driver does not use +interrupts. diff -Nru a/Documentation/i2c/busses/i2c-ali1563 b/Documentation/i2c/busses/i2c-ali1563 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-ali1563 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,27 @@ +Kernel driver i2c-ali1563 + +Supported adapters: + * Acer Labs, Inc. ALI 1563 (south bridge) + Datasheet: Now under NDA + http://www.ali.com.tw/eng/support/datasheet_request.php + +Author: Patrick Mochel + +Description +----------- + +This is the driver for the SMB Host controller on Acer Labs Inc. (ALI) +M1563 South Bridge. + +For an overview of these chips see http://www.acerlabs.com + +The M1563 southbridge is deceptively similar to the M1533, with a few +notable exceptions. One of those happens to be the fact they upgraded the +i2c core to be SMBus 2.0 compliant, and happens to be almost identical to +the i2c controller found in the Intel 801 south bridges. + +Features +-------- + +This driver controls the SMB Host only. This driver does not use +interrupts. diff -Nru a/Documentation/i2c/busses/i2c-ali15x3 b/Documentation/i2c/busses/i2c-ali15x3 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-ali15x3 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,112 @@ +Kernel driver i2c-ali15x3 + +Supported adapters: + * Acer Labs, Inc. ALI 1533 and 1543C (south bridge) + Datasheet: Now under NDA + http://www.ali.com.tw/eng/support/datasheet_request.php + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Mark D. Studebaker + +Module Parameters +----------------- + +* force_addr: int + Initialize the base address of the i2c controller + + +Notes +----- + +The force_addr parameter is useful for boards that don't set the address in +the BIOS. Does not do a PCI force; the device must still be present in +lspci. Don't use this unless the driver complains that the base address is +not set. + +Example: 'modprobe i2c-ali15x3 force_addr=0xe800' + +SMBus periodically hangs on ASUS P5A motherboards and can only be cleared +by a power cycle. Cause unknown (see Issues below). + + +Description +----------- + +This is the driver for the SMB Host controller on Acer Labs Inc. (ALI) +M1541 and M1543C South Bridges. + +The M1543C is a South bridge for desktop systems. +The M1541 is a South bridge for portable systems. +They are part of the following ALI chipsets: + + * "Aladdin Pro 2" includes the M1621 Slot 1 North bridge with AGP and + 100MHz CPU Front Side bus + * "Aladdin V" includes the M1541 Socket 7 North bridge with AGP and 100MHz + CPU Front Side bus + Some Aladdin V motherboards: + Asus P5A + Atrend ATC-5220 + BCM/GVC VP1541 + Biostar M5ALA + Gigabyte GA-5AX (** Generally doesn't work because the BIOS doesn't + enable the 7101 device! **) + Iwill XA100 Plus + Micronics C200 + Microstar (MSI) MS-5169 + + * "Aladdin IV" includes the M1541 Socket 7 North bridge + with host bus up to 83.3 MHz. + +For an overview of these chips see http://www.acerlabs.com. At this time the +full data sheets on the web site are password protected, however if you +contact the ALI office in San Jose they may give you the password. + +The M1533/M1543C devices appear as FOUR separate devices on the PCI bus. An +output of lspci will show something similar to the following: + + 00:02.0 USB Controller: Acer Laboratories Inc. M5237 (rev 03) + 00:03.0 Bridge: Acer Laboratories Inc. M7101 <= THIS IS THE ONE WE NEED + 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 (rev c3) + 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 (rev c1) + +** IMPORTANT ** +** If you have a M1533 or M1543C on the board and you get +** "ali15x3: Error: Can't detect ali15x3!" +** then run lspci. +** If you see the 1533 and 5229 devices but NOT the 7101 device, +** then you must enable ACPI, the PMU, SMB, or something similar +** in the BIOS. +** The driver won't work if it can't find the M7101 device. + +The SMB controller is part of the M7101 device, which is an ACPI-compliant +Power Management Unit (PMU). + +The whole M7101 device has to be enabled for the SMB to work. You can't +just enable the SMB alone. The SMB and the ACPI have separate I/O spaces. +We make sure that the SMB is enabled. We leave the ACPI alone. + +Features +-------- + +This driver controls the SMB Host only. The SMB Slave +controller on the M15X3 is not enabled. This driver does not use +interrupts. + + +Issues +------ + +This driver requests the I/O space for only the SMB +registers. It doesn't use the ACPI region. + +On the ASUS P5A motherboard, there are several reports that +the SMBus will hang and this can only be resolved by +powering off the computer. It appears to be worse when the board +gets hot, for example under heavy CPU load, or in the summer. +There may be electrical problems on this board. +On the P5A, the W83781D sensor chip is on both the ISA and +SMBus. Therefore the SMBus hangs can generally be avoided +by accessing the W83781D on the ISA bus only. + diff -Nru a/Documentation/i2c/busses/i2c-amd756 b/Documentation/i2c/busses/i2c-amd756 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-amd756 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,25 @@ +Kernel driver i2c-amd756 + +Supported adapters: + * AMD 756 + * AMD 766 + * AMD 768 + * AMD 8111 + Datasheets: Publicly available on AMD website + + * nVidia nForce + Datasheet: Unavailable + +Authors: + Frodo Looijaard , + Philip Edelbrock + +Description +----------- + +This driver supports the AMD 756, 766, 768 and 8111 Peripheral Bus +Controllers, and the nVidia nForce. + +Note that for the 8111, there are two SMBus adapters. The SMBus 1.0 adapter +is supported by this driver, and the SMBus 2.0 adapter is supported by the +i2c-amd8111 driver. diff -Nru a/Documentation/i2c/busses/i2c-amd8111 b/Documentation/i2c/busses/i2c-amd8111 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-amd8111 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,41 @@ +Kernel driver i2c-adm8111 + +Supported adapters: + * AMD-8111 SMBus 2.0 PCI interface + +Datasheets: + AMD datasheet not yet available, but almost everything can be found + in publically available ACPI 2.0 specification, which the adapter + follows. + +Author: Vojtech Pavlik + +Description +----------- + +If you see something like this: + +00:07.2 SMBus: Advanced Micro Devices [AMD] AMD-8111 SMBus 2.0 (rev 02) + Subsystem: Advanced Micro Devices [AMD] AMD-8111 SMBus 2.0 + Flags: medium devsel, IRQ 19 + I/O ports at d400 [size=32] + +in your 'lspci -v', then this driver is for your chipset. + +Process Call Support +-------------------- + +Supported. + +SMBus 2.0 Support +----------------- + +Supported. Both PEC and block process call support is implemented. Slave +mode or host notification are not yet implemented. + +Notes +----- + +Note that for the 8111, there are two SMBus adapters. The SMBus 2.0 adapter +is supported by this driver, and the SMBus 1.0 adapter is supported by the +i2c-amd756 driver. diff -Nru a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-i801 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,80 @@ +Kernel driver i2c-i801 + +Supported adapters: + * Intel 82801AA and 82801AB (ICH and ICH0 - part of the + '810' and '810E' chipsets) + * Intel 82801BA (ICH2 - part of the '815E' chipset) + * Intel 82801CA/CAM (ICH3) + * Intel 82801DB (ICH4) (HW PEC supported, 32 byte buffer not supported) + * Intel 82801EB/ER (ICH5) (HW PEC supported, 32 byte buffer not supported) + * Intel 6300ESB + * Intel 82801FB/FR/FW/FRW (ICH6) + * Intel ICH7 + Datasheets: Publicly available at the Intel website + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Mark Studebaker + + +Module Parameters +----------------- + +* force_addr: int + Forcibly enable the ICH at the given address. EXTREMELY DANGEROUS! + + +Description +----------- + +The ICH (properly known as the 82801AA), ICH0 (82801AB), ICH2 (82801BA), +ICH3 (82801CA/CAM) and later devices are Intel chips that are a part of +Intel's '810' chipset for Celeron-based PCs, '810E' chipset for +Pentium-based PCs, '815E' chipset, and others. + +The ICH chips contain at least SEVEN separate PCI functions in TWO logical +PCI devices. An output of lspci will show something similar to the +following: + + 00:1e.0 PCI bridge: Intel Corporation: Unknown device 2418 (rev 01) + 00:1f.0 ISA bridge: Intel Corporation: Unknown device 2410 (rev 01) + 00:1f.1 IDE interface: Intel Corporation: Unknown device 2411 (rev 01) + 00:1f.2 USB Controller: Intel Corporation: Unknown device 2412 (rev 01) + 00:1f.3 Unknown class [0c05]: Intel Corporation: Unknown device 2413 (rev 01) + +The SMBus controller is function 3 in device 1f. Class 0c05 is SMBus Serial +Controller. + +If you do NOT see the 24x3 device at function 3, and you can't figure out +any way in the BIOS to enable it, + +The ICH chips are quite similar to Intel's PIIX4 chip, at least in the +SMBus controller. + +See the file i2c-piix4 for some additional information. + + +Process Call Support +-------------------- + +Not supported. + + +I2C Block Read Support +---------------------- + +Not supported at the moment. + + +SMBus 2.0 Support +----------------- + +The 82801DB (ICH4) and later chips support several SMBus 2.0 features. + +********************** +The lm_sensors project gratefully acknowledges the support of Texas +Instruments in the initial development of this driver. + +The lm_sensors project gratefully acknowledges the support of Intel in the +development of SMBus 2.0 / ICH4 features of this driver. diff -Nru a/Documentation/i2c/busses/i2c-i810 b/Documentation/i2c/busses/i2c-i810 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-i810 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,46 @@ +Kernel driver i2c-i810 + +Supported adapters: + * Intel 82810, 82810-DC100, 82810E, and 82815 (GMCH) + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Kyösti Mälkki , + Ralph Metzler , + Mark D. Studebaker + +Main contact: Mark Studebaker + +Description +----------- + +WARNING: If you have an '810' or '815' motherboard, your standard I2C +temperature sensors are most likely on the 801's I2C bus. You want the +i2c-i801 driver for those, not this driver. + +Now for the i2c-i810... + +The GMCH chip contains two I2C interfaces. + +The first interface is used for DDC (Data Display Channel) which is a +serial channel through the VGA monitor connector to a DDC-compliant +monitor. This interface is defined by the Video Electronics Standards +Association (VESA). The standards are available for purchase at +http://www.vesa.org . + +The second interface is a general-purpose I2C bus. It may be connected to a +TV-out chip such as the BT869 or possibly to a digital flat-panel display. + +Features +-------- + +Both busses use the i2c-algo-bit driver for 'bit banging' +and support for specific transactions is provided by i2c-algo-bit. + +Issues +------ + +If you enable bus testing in i2c-algo-bit (insmod i2c-algo-bit bit_test=1), +the test may fail; if so, the i2c-i810 driver won't be inserted. However, +we think this has been fixed. diff -Nru a/Documentation/i2c/busses/i2c-nforce2 b/Documentation/i2c/busses/i2c-nforce2 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-nforce2 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,41 @@ +Kernel driver i2c-nforce2 + +Supported adapters: + * nForce2 MCP 10de:0064 + * nForce2 Ultra 400 MCP 10de:0084 + * nForce3 Pro150 MCP 10de:00D4 + * nForce3 250Gb MCP 10de:00E4 + * nForce4 MCP 10de:0052 + +Datasheet: not publically available, but seems to be similar to the + AMD-8111 SMBus 2.0 adapter. + +Authors: + Hans-Frieder Vogt , + Thomas Leibold , + Patrick Dreker + +Description +----------- + +i2c-nforce2 is a driver for the SMBuses included in the nVidia nForce2 MCP. + +If your 'lspci -v' listing shows something like the following, + +00:01.1 SMBus: nVidia Corporation: Unknown device 0064 (rev a2) + Subsystem: Asustek Computer, Inc.: Unknown device 0c11 + Flags: 66Mhz, fast devsel, IRQ 5 + I/O ports at c000 [size=32] + Capabilities: + +then this driver should support the SMBuses of your motherboard. + + +Notes +----- + +The SMBus adapter in the nForce2 chipset seems to be very similar to the +SMBus 2.0 adapter in the AMD-8111 southbridge. However, I could only get +the driver to work with direct I/O access, which is different to the EC +interface of the AMD-8111. Tested on Asus A7N8X. The ACPI DSDT table of the +Asus A7N8X lists two SMBuses, both of which are supported by this driver. diff -Nru a/Documentation/i2c/busses/i2c-parport b/Documentation/i2c/busses/i2c-parport --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-parport 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,154 @@ +Kernel driver i2c-parport + +Author: Jean Delvare + +This is a unified driver for several i2c-over-parallel-port adapters, +such as the ones made by Philips, Velleman or ELV. This driver is +meant as a replacement for the older, individual drivers: + * i2c-philips-par + * i2c-elv + * i2c-velleman + * video/i2c-parport (NOT the same as this one, dedicated to home brew + teletext adapters) + +It currently supports the following devices: + * Philips adapter + * home brew teletext adapter + * Velleman K8000 adapter + * ELV adapter + * Analog Devices evaluation boards (ADM1025, ADM1030, ADM1031, ADM1032) + +These devices use different pinout configurations, so you have to tell +the driver what you have, using the type module parameter. There is no +way to autodetect the devices. Support for different pinout configurations +can be easily added when needed. + + +Building your own adapter +------------------------- + +If you want to build you own i2c-over-parallel-port adapter, here is +a sample electronics schema (credits go to Sylvain Munaut): + +Device PC +Side ___________________Vdd (+) Side + | | | + --- --- --- + | | | | | | + |R| |R| |R| + | | | | | | + --- --- --- + | | | + | | /| | +SCL ----------x--------o |-----------x------------------- pin 2 + | \| | | + | | | + | |\ | | +SDA ----------x----x---| o---x--------------------------- pin 13 + | |/ | + | | + | /| | + ---------o |----------------x-------------- pin 3 + \| | | + | | + --- --- + | | | | + |R| |R| + | | | | + --- --- + | | + ### ### + GND GND + +Remarks: + - This is the exact pinout and electronics used on the Analog Devices + evaluation boards. + /| + - All inverters -o |- must be 74HC05, they must be open collector output. + \| + - All resitors are 10k. + - Pins 18-25 of the parallel port connected to GND. + - Pins 4-9 (D2-D7) could be used as VDD is the driver drives them high. + The ADM1032 evaluation board uses D4-D7. Beware that the amount of + current you can draw from the parallel port is limited. Also note that + all connected lines MUST BE driven at the same state, else you'll short + circuit the output buffers! So plugging the I2C adapter after loading + the i2c-parport module might be a good safety since data line state + prior to init may be unknown. + - This is 5V! + - Obviously you cannot read SCL (so it's not really standard-compliant). + Pretty easy to add, just copy the SDA part and use another input pin. + That would give (ELV compatible pinout): + + +Device PC +Side ______________________________Vdd (+) Side + | | | | + --- --- --- --- + | | | | | | | | + |R| |R| |R| |R| + | | | | | | | | + --- --- --- --- + | | | | + | | |\ | | +SCL ----------x--------x--| o---x------------------------ pin 15 + | | |/ | + | | | + | | /| | + | ---o |-------------x-------------- pin 2 + | \| | | + | | | + | | | + | |\ | | +SDA ---------------x---x--| o--------x------------------- pin 10 + | |/ | + | | + | /| | + ---o |------------------x--------- pin 3 + \| | | + | | + --- --- + | | | | + |R| |R| + | | | | + --- --- + | | + ### ### + GND GND + + +If possible, you should use the same pinout configuration as existing +adapters do, so you won't even have to change the code. + + +Similar (but different) drivers +------------------------------- + +This driver is NOT the same as the i2c-pport driver found in the i2c +package. The i2c-pport driver makes use of modern parallel port features so +that you don't need additional electronics. It has other restrictions +however, and was not ported to Linux 2.6 (yet). + +This driver is also NOT the same as the i2c-pcf-epp driver found in the +lm_sensors package. The i2c-pcf-epp driver doesn't use the parallel port as +an I2C bus directly. Instead, it uses it to control an external I2C bus +master. That driver was not ported to Linux 2.6 (yet) either. + + +Legacy documentation for Velleman adapter +----------------------------------------- + +Useful links: +Velleman http://www.velleman.be/ +Velleman K8000 Howto http://howto.htlw16.ac.at/k8000-howto.html + +The project has lead to new libs for the Velleman K8000 and K8005: + LIBK8000 v1.99.1 and LIBK8005 v0.21 +With these libs, you can control the K8000 interface card and the K8005 +stepper motor card with the simple commands which are in the original +Velleman software, like SetIOchannel, ReadADchannel, SendStepCCWFull and +many more, using /dev/velleman. + http://home.wanadoo.nl/hihihi/libk8000.htm + http://home.wanadoo.nl/hihihi/libk8005.htm + http://struyve.mine.nu:8080/index.php?block=k8000 + http://sourceforge.net/projects/libk8005/ diff -Nru a/Documentation/i2c/busses/i2c-parport-light b/Documentation/i2c/busses/i2c-parport-light --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-parport-light 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,11 @@ +Kernel driver i2c-parport-light + +Author: Jean Delvare + +This driver is a light version of i2c-parport. It doesn't depend +on the parport driver, and uses direct I/O access instead. This might be +prefered on embedded systems where wasting memory for the clean but heavy +parport handling is not an option. The drawback is a reduced portability +and the impossibility to daisy-chain other parallel port devices. + +Please see i2c-parport for documentation. diff -Nru a/Documentation/i2c/busses/i2c-pca-isa b/Documentation/i2c/busses/i2c-pca-isa --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-pca-isa 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,23 @@ +Kernel driver i2c-pca-isa + +Supported adapters: +This driver supports ISA boards using the Philips PCA 9564 +Parallel bus to I2C bus controller + +Author: Ian Campbell , Arcom Control Systems + +Module Parameters +----------------- + +* base int + I/O base address +* irq int + IRQ interrupt +* clock int + Clock rate as described in table 1 of PCA9564 datasheet + +Description +----------- + +This driver supports ISA boards using the Philips PCA 9564 +Parallel bus to I2C bus controller diff -Nru a/Documentation/i2c/busses/i2c-piix4 b/Documentation/i2c/busses/i2c-piix4 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-piix4 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,72 @@ +Kernel driver i2c-piix4 + +Supported adapters: + * Intel 82371AB PIIX4 and PIIX4E + * Intel 82443MX (440MX) + Datasheet: Publicly available at the Intel website + * ServerWorks OSB4, CSB5 and CSB6 southbridges + Datasheet: Only available via NDA from ServerWorks + * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge + Datasheet: Publicly available at the SMSC website http://www.smsc.com + +Authors: + Frodo Looijaard + Philip Edelbrock + + +Module Parameters +----------------- + +* force: int + Forcibly enable the PIIX4. DANGEROUS! +* force_addr: int + Forcibly enable the PIIX4 at the given address. EXTREMELY DANGEROUS! +* fix_hstcfg: int + Fix config register. Needed on some boards (Force CPCI735). + + +Description +----------- + +The PIIX4 (properly known as the 82371AB) is an Intel chip with a lot of +functionality. Among other things, it implements the PCI bus. One of its +minor functions is implementing a System Management Bus. This is a true +SMBus - you can not access it on I2C levels. The good news is that it +natively understands SMBus commands and you do not have to worry about +timing problems. The bad news is that non-SMBus devices connected to it can +confuse it mightily. Yes, this is known to happen... + +Do 'lspci -v' and see whether it contains an entry like this: + +0000:00:02.3 Bridge: Intel Corp. 82371AB/EB/MB PIIX4 ACPI (rev 02) + Flags: medium devsel, IRQ 9 + +Bus and device numbers may differ, but the function number must be +identical (like many PCI devices, the PIIX4 incorporates a number of +different 'functions', which can be considered as separate devices). If you +find such an entry, you have a PIIX4 SMBus controller. + +On some computers (most notably, some Dells), the SMBus is disabled by +default. If you use the insmod parameter 'force=1', the kernel module will +try to enable it. THIS IS VERY DANGEROUS! If the BIOS did not set up a +correct address for this module, you could get in big trouble (read: +crashes, data corruption, etc.). Try this only as a last resort (try BIOS +updates first, for example), and backup first! An even more dangerous +option is 'force_addr='. This will not only enable the PIIX4 like +'force' foes, but it will also set a new base I/O port address. The SMBus +parts of the PIIX4 needs a range of 8 of these addresses to function +correctly. If these addresses are already reserved by some other device, +you will get into big trouble! DON'T USE THIS IF YOU ARE NOT VERY SURE +ABOUT WHAT YOU ARE DOING! + +The PIIX4E is just an new version of the PIIX4; it is supported as well. +The PIIX/PIIX3 does not implement an SMBus or I2C bus, so you can't use +this driver on those mainboards. + +The ServerWorks Southbridges, the Intel 440MX, and the Victory766 are +identical to the PIIX4 in I2C/SMBus support. + +A few OSB4 southbridges are known to be misconfigured by the BIOS. In this +case, you have you use the fix_hstcfg module parameter. Do not use it +unless you know you have to, because in some cases it also breaks +configuration on southbridges that don't need it. diff -Nru a/Documentation/i2c/busses/i2c-prosavage b/Documentation/i2c/busses/i2c-prosavage --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-prosavage 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,23 @@ +Kernel driver i2c-prosavage + +Supported adapters: + + S3/VIA KM266/VT8375 aka ProSavage8 + S3/VIA KM133/VT8365 aka Savage4 + +Author: Henk Vergonet + +Description +----------- + +The Savage4 chips contain two I2C interfaces (aka a I2C 'master' or +'host'). + +The first interface is used for DDC (Data Display Channel) which is a +serial channel through the VGA monitor connector to a DDC-compliant +monitor. This interface is defined by the Video Electronics Standards +Association (VESA). The standards are available for purchase at +http://www.vesa.org . The second interface is a general-purpose I2C bus. + +Usefull for gaining access to the TV Encoder chips. + diff -Nru a/Documentation/i2c/busses/i2c-savage4 b/Documentation/i2c/busses/i2c-savage4 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-savage4 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,26 @@ +Kernel driver i2c-savage4 + +Supported adapters: + * Savage4 + * Savage2000 + +Authors: + Alexander Wold , + Mark D. Studebaker + +Description +----------- + +The Savage4 chips contain two I2C interfaces (aka a I2C 'master' +or 'host'). + +The first interface is used for DDC (Data Display Channel) which is a +serial channel through the VGA monitor connector to a DDC-compliant +monitor. This interface is defined by the Video Electronics Standards +Association (VESA). The standards are available for purchase at +http://www.vesa.org . The DDC bus is not yet supported because its register +is not directly memory-mapped. + +The second interface is a general-purpose I2C bus. This is the only +interface supported by the driver at the moment. + diff -Nru a/Documentation/i2c/busses/i2c-sis5595 b/Documentation/i2c/busses/i2c-sis5595 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-sis5595 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,59 @@ +Kernel driver i2c-sis5595 + +Authors: + Frodo Looijaard , + Mark D. Studebaker , + Philip Edelbrock + +Supported adapters: + * Silicon Integrated Systems Corp. SiS5595 Southbridge + Datasheet: Publicly available at the Silicon Integrated Systems Corp. site. + +Note: all have mfr. ID 0x1039. + + SUPPORTED PCI ID + 5595 0008 + + Note: these chips contain a 0008 device which is incompatible with the + 5595. We recognize these by the presence of the listed + "blacklist" PCI ID and refuse to load. + + NOT SUPPORTED PCI ID BLACKLIST PCI ID + 540 0008 0540 + 550 0008 0550 + 5513 0008 5511 + 5581 0008 5597 + 5582 0008 5597 + 5597 0008 5597 + 5598 0008 5597/5598 + 630 0008 0630 + 645 0008 0645 + 646 0008 0646 + 648 0008 0648 + 650 0008 0650 + 651 0008 0651 + 730 0008 0730 + 735 0008 0735 + 745 0008 0745 + 746 0008 0746 + +Module Parameters +----------------- + +* force_addr=0xaddr Set the I/O base address. Useful for boards + that don't set the address in the BIOS. Does not do a + PCI force; the device must still be present in lspci. + Don't use this unless the driver complains that the + base address is not set. + +Description +----------- + +i2c-sis5595 is a true SMBus host driver for motherboards with the SiS5595 +southbridges. + +WARNING: If you are trying to access the integrated sensors on the SiS5595 +chip, you want the sis5595 driver for those, not this driver. This driver +is a BUS driver, not a CHIP driver. A BUS driver is used by other CHIP +drivers to access chips on the bus. + diff -Nru a/Documentation/i2c/busses/i2c-sis630 b/Documentation/i2c/busses/i2c-sis630 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-sis630 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,49 @@ +Kernel driver i2c-sis630 + +Supported adapters: + * Silicon Integrated Systems Corp (SiS) + 630 chipset (Datasheet: available at http://amalysh.bei.t-online.de/docs/SIS/) + 730 chipset + * Possible other SiS chipsets ? + +Author: Alexander Malysh + +Module Parameters +----------------- + +* force = [1|0] Forcibly enable the SIS630. DANGEROUS! + This can be interesting for chipsets not named + above to check if it works for you chipset, but DANGEROUS! + +* high_clock = [1|0] Forcibly set Host Master Clock to 56KHz (default, + what your BIOS use). DANGEROUS! This should be a bit + faster, but freeze some systems (i.e. my Laptop). + + +Description +----------- + +This SMBus only driver is known to work on motherboards with the above +named chipsets. + +If you see something like this: + +00:00.0 Host bridge: Silicon Integrated Systems [SiS] 630 Host (rev 31) +00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513 + +or like this: + +00:00.0 Host bridge: Silicon Integrated Systems [SiS] 730 Host (rev 02) +00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513 + +in your 'lspci' output , then this driver is for your chipset. + +Thank You +--------- +Philip Edelbrock +- testing SiS730 support +Mark M. Hoffman +- bug fixes + +To anyone else which I forgot here ;), thanks! + diff -Nru a/Documentation/i2c/busses/i2c-sis69x b/Documentation/i2c/busses/i2c-sis69x --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-sis69x 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,73 @@ +Kernel driver i2c-sis96x + +Replaces 2.4.x i2c-sis645 + +Supported adapters: + * Silicon Integrated Systems Corp (SiS) + Any combination of these host bridges: + 645, 645DX (aka 646), 648, 650, 651, 655, 735, 745, 746 + and these south bridges: + 961, 962, 963(L) + +Author: Mark M. Hoffman + +Description +----------- + +This SMBus only driver is known to work on motherboards with the above +named chipset combinations. The driver was developed without benefit of a +proper datasheet from SiS. The SMBus registers are assumed compatible with +those of the SiS630, although they are located in a completely different +place. Thanks to Alexander Malysh for providing the +SiS630 datasheet (and driver). + +The command "lspci" as root should produce something like these lines: + +00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645 +00:02.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513 +00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016 + +or perhaps this... + +00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645 +00:02.0 ISA bridge: Silicon Integrated Systems [SiS]: Unknown device 0961 +00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016 + +(kernel versions later than 2.4.18 may fill in the "Unknown"s) + +If you cant see it please look on quirk_sis_96x_smbus +(drivers/pci/quirks.c) (also if southbridge detection fails) + +I suspect that this driver could be made to work for the following SiS +chipsets as well: 635, and 635T. If anyone owns a board with those chips +AND is willing to risk crashing & burning an otherwise well-behaved kernel +in the name of progress... please contact me at or +via the project's mailing list: . Please +send bug reports and/or success stories as well. + + +TO DOs +------ + +* The driver does not support SMBus block reads/writes; I may add them if a +scenario is found where they're needed. + + +Thank You +--------- + +Mark D. Studebaker + - design hints and bug fixes +Alexander Maylsh + - ditto, plus an important datasheet... almost the one I really wanted +Hans-Günter Lütke Uphues + - patch for SiS735 +Robert Zwerus + - testing for SiS645DX +Kianusch Sayah Karadji + - patch for SiS645DX/962 +Ken Healy + - patch for SiS655 + +To anyone else who has written w/ feedback, thanks! + diff -Nru a/Documentation/i2c/busses/i2c-via b/Documentation/i2c/busses/i2c-via --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-via 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,34 @@ +Kernel driver i2c-via + +Supported adapters: + * VIA Technologies, InC. VT82C586B + Datasheet: Publicly available at the VIA website + +Author: Kyösti Mälkki + +Description +----------- + +i2c-via is an i2c bus driver for motherboards with VIA chipset. + +The following VIA pci chipsets are supported: + - MVP3, VP3, VP2/97, VPX/97 + - others with South bridge VT82C586B + +Your lspci listing must show this : + + Bridge: VIA Technologies, Inc. VT82C586B ACPI (rev 10) + + Problems? + + Q: You have VT82C586B on the motherboard, but not in the listing. + + A: Go to your BIOS setup, section PCI devices or similar. + Turn USB support on, and try again. + + Q: No error messages, but still i2c doesn't seem to work. + + A: This can happen. This driver uses the pins VIA recommends in their + datasheets, but there are several ways the motherboard manufacturer + can actually wire the lines. + diff -Nru a/Documentation/i2c/busses/i2c-viapro b/Documentation/i2c/busses/i2c-viapro --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-viapro 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,47 @@ +Kernel driver i2c-viapro + +Supported adapters: + * VIA Technologies, Inc. VT82C596A/B + Datasheet: Sometimes available at the VIA website + + * VIA Technologies, Inc. VT82C686A/B + Datasheet: Sometimes available at the VIA website + + * VIA Technologies, Inc. VT8231, VT8233, VT8233A, VT8235, VT8237 + Datasheet: available on request from Via + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Kyösti Mälkki , + Mark D. Studebaker + +Module Parameters +----------------- + +* force: int + Forcibly enable the SMBus controller. DANGEROUS! +* force_addr: int + Forcibly enable the SMBus at the given address. EXTREMELY DANGEROUS! + +Description +----------- + +i2c-viapro is a true SMBus host driver for motherboards with one of the +supported VIA southbridges. + +Your lspci -n listing must show one of these : + + device 1106:3050 (VT82C596 function 3) + device 1106:3051 (VT82C596 function 3) + device 1106:3057 (VT82C686 function 4) + device 1106:3074 (VT8233) + device 1106:3147 (VT8233A) + device 1106:8235 (VT8231) + devide 1106:3177 (VT8235) + devide 1106:3227 (VT8237) + +If none of these show up, you should look in the BIOS for settings like +enable ACPI / SMBus or even USB. + + diff -Nru a/Documentation/i2c/busses/i2c-voodoo3 b/Documentation/i2c/busses/i2c-voodoo3 --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/i2c-voodoo3 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,62 @@ +Kernel driver i2c-voodoo3 + +Supported adapters: + * 3dfx Voodoo3 based cards + * Voodoo Banshee based cards + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Ralph Metzler , + Mark D. Studebaker + +Main contact: Philip Edelbrock + +The code is based upon Ralph's test code (he did the hard stuff ;') + +Description +----------- + +The 3dfx Voodoo3 chip contains two I2C interfaces (aka a I2C 'master' or +'host'). + +The first interface is used for DDC (Data Display Channel) which is a +serial channel through the VGA monitor connector to a DDC-compliant +monitor. This interface is defined by the Video Electronics Standards +Association (VESA). The standards are available for purchase at +http://www.vesa.org . + +The second interface is a general-purpose I2C bus. The intent by 3dfx was +to allow manufacturers to add extra chips to the video card such as a +TV-out chip such as the BT869 or possibly even I2C based temperature +sensors like the ADM1021 or LM75. + +Stability +--------- + +Seems to be stable on the test machine, but needs more testing on other +machines. Simultaneous accesses of the DDC and I2C busses may cause errors. + +Supported Devices +----------------- + +Specifically, this driver was written and tested on the '3dfx Voodoo3 AGP +3000' which has a tv-out feature (s-video or composite). According to the +docs and discussions, this code should work for any Voodoo3 based cards as +well as Voodoo Banshee based cards. The DDC interface has been tested on a +Voodoo Banshee card. + +Issues +------ + +Probably many, but it seems to work OK on my system. :') + + +External Device Connection +-------------------------- + +The digital video input jumpers give availability to the I2C bus. +Specifically, pins 13 and 25 (bottom row middle, and bottom right-end) are +the I2C clock and I2C data lines, respectively. +5V and GND are probably +also easily available making the addition of extra I2C/SMBus devices easy +to implement. diff -Nru a/Documentation/i2c/busses/scx200_acb b/Documentation/i2c/busses/scx200_acb --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/Documentation/i2c/busses/scx200_acb 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,14 @@ +Kernel driver scx200_acb + +Author: Christer Weinigel + +Module Parameters +----------------- + +* base: int + Base addresses for the ACCESS.bus controllers + +Description +----------- + +Enable the use of the ACCESS.bus controllers of a SCx200 processor. diff -Nru a/Documentation/i2c/i2c-parport b/Documentation/i2c/i2c-parport --- a/Documentation/i2c/i2c-parport 2005-03-23 19:38:41 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,156 +0,0 @@ -================== -i2c-parport driver -================== - -2004-07-06, Jean Delvare - -This is a unified driver for several i2c-over-parallel-port adapters, -such as the ones made by Philips, Velleman or ELV. This driver is -meant as a replacement for the older, individual drivers: - * i2c-philips-par - * i2c-elv - * i2c-velleman - * video/i2c-parport (NOT the same as this one, dedicated to home brew - teletext adapters) - -It currently supports the following devices: - * Philips adapter - * home brew teletext adapter - * Velleman K8000 adapter - * ELV adapter - * Analog Devices evaluation boards (ADM1025, ADM1030, ADM1031, ADM1032) - -These devices use different pinout configurations, so you have to tell -the driver what you have, using the type module parameter. There is no -way to autodetect the devices. Support for different pinout configurations -can be easily added when needed. - - -Building your own adapter -------------------------- - -If you want to build you own i2c-over-parallel-port adapter, here is -a sample electronics schema (credits go to Sylvain Munaut): - -Device PC -Side ___________________Vdd (+) Side - | | | - --- --- --- - | | | | | | - |R| |R| |R| - | | | | | | - --- --- --- - | | | - | | /| | -SCL ----------x--------o |-----------x------------------- pin 2 - | \| | | - | | | - | |\ | | -SDA ----------x----x---| o---x--------------------------- pin 13 - | |/ | - | | - | /| | - ---------o |----------------x-------------- pin 3 - \| | | - | | - --- --- - | | | | - |R| |R| - | | | | - --- --- - | | - ### ### - GND GND - -Remarks: - - This is the exact pinout and electronics used on the Analog Devices - evaluation boards. - /| - - All inverters -o |- must be 74HC05, they must be open collector output. - \| - - All resitors are 10k. - - Pins 18-25 of the parallel port connected to GND. - - Pins 4-9 (D2-D7) could be used as VDD is the driver drives them high. - The ADM1032 evaluation board uses D4-D7. Beware that the amount of - current you can draw from the parallel port is limited. Also note that - all connected lines MUST BE driven at the same state, else you'll short - circuit the output buffers! So plugging the I2C adapter after loading - the i2c-parport module might be a good safety since data line state - prior to init may be unknown. - - This is 5V! - - Obviously you cannot read SCL (so it's not really standard-compliant). - Pretty easy to add, just copy the SDA part and use another input pin. - That would give (ELV compatible pinout): - - -Device PC -Side ______________________________Vdd (+) Side - | | | | - --- --- --- --- - | | | | | | | | - |R| |R| |R| |R| - | | | | | | | | - --- --- --- --- - | | | | - | | |\ | | -SCL ----------x--------x--| o---x------------------------ pin 15 - | | |/ | - | | | - | | /| | - | ---o |-------------x-------------- pin 2 - | \| | | - | | | - | | | - | |\ | | -SDA ---------------x---x--| o--------x------------------- pin 10 - | |/ | - | | - | /| | - ---o |------------------x--------- pin 3 - \| | | - | | - --- --- - | | | | - |R| |R| - | | | | - --- --- - | | - ### ### - GND GND - - -If possible, you should use the same pinout configuration as existing -adapters do, so you won't even have to change the code. - - -Similar (but different) drivers -------------------------------- - -This driver is NOT the same as the i2c-pport driver found in the i2c package. -The i2c-pport driver makes use of modern parallel port features so that -you don't need additional electronics. It has other restrictions however, and -was not ported to Linux 2.6 (yet). - -This driver is also NOT the same as the i2c-pcf-epp driver found in the -lm_sensors package. The i2c-pcf-epp driver doesn't use the parallel port -as an I2C bus directly. Instead, it uses it to control an external I2C bus -master. That driver was not ported to Linux 2.6 (yet) either. - - -Legacy documentation for Velleman adapter ------------------------------------------ - -Useful links: -Velleman http://www.velleman.be/ -Velleman K8000 Howto http://howto.htlw16.ac.at/k8000-howto.html - -The project has lead to new libs for the Velleman K8000 and K8005: - LIBK8000 v1.99.1 and LIBK8005 v0.21 -With these libs, you can control the K8000 interface card and the K8005 -stepper motor card with the simple commands which are in the original -Velleman software, like SetIOchannel, ReadADchannel, SendStepCCWFull and -many more, using /dev/velleman. - http://home.wanadoo.nl/hihihi/libk8000.htm - http://home.wanadoo.nl/hihihi/libk8005.htm - http://struyve.mine.nu:8080/index.php?block=k8000 - http://sourceforge.net/projects/libk8005/ diff -Nru a/drivers/Kconfig b/drivers/Kconfig --- a/drivers/Kconfig 2005-03-23 19:38:41 -08:00 +++ b/drivers/Kconfig 2005-03-23 19:38:41 -08:00 @@ -4,6 +4,8 @@ source "drivers/base/Kconfig" +source "drivers/connector/Kconfig" + source "drivers/mtd/Kconfig" source "drivers/parport/Kconfig" @@ -43,6 +45,8 @@ source "drivers/i2c/Kconfig" source "drivers/w1/Kconfig" + +source "drivers/superio/Kconfig" source "drivers/misc/Kconfig" diff -Nru a/drivers/Makefile b/drivers/Makefile --- a/drivers/Makefile 2005-03-23 19:38:41 -08:00 +++ b/drivers/Makefile 2005-03-23 19:38:41 -08:00 @@ -17,6 +17,8 @@ # default. obj-y += char/ +obj-$(CONFIG_CONNECTOR) += connector/ + # i810fb and intelfb depend on char/agp/ obj-$(CONFIG_FB_I810) += video/i810/ obj-$(CONFIG_FB_INTEL) += video/intelfb/ @@ -52,6 +54,7 @@ obj-$(CONFIG_I2O) += message/ obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_W1) += w1/ +obj-$(CONFIG_SC_SUPERIO) += superio/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ diff -Nru a/drivers/connector/Kconfig b/drivers/connector/Kconfig --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/connector/Kconfig 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,13 @@ +menu "Connector - unified userspace <-> kernelspace linker" + +config CONNECTOR + tristate "Connector - unified userspace <-> kernelspace linker" + depends on NET + ---help--- + This is unified userspace <-> kernelspace connector working on top + of the netlink socket protocol. + + Connector support can also be built as a module. If so, the module + will be called cn.ko. + +endmenu diff -Nru a/drivers/connector/Makefile b/drivers/connector/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/connector/Makefile 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,2 @@ +obj-$(CONFIG_CONNECTOR) += cn.o +cn-objs := cn_queue.o connector.o diff -Nru a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/connector/cn_queue.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,231 @@ +/* + * cn_queue.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void cn_queue_wrapper(void *data) +{ + struct cn_callback_entry *cbq = (struct cn_callback_entry *)data; + + smp_mb__before_atomic_inc(); + atomic_inc(&cbq->cb->refcnt); + smp_mb__after_atomic_inc(); + cbq->cb->callback(cbq->cb->priv); + smp_mb__before_atomic_inc(); + atomic_dec(&cbq->cb->refcnt); + smp_mb__after_atomic_inc(); + + cbq->destruct_data(cbq->ddata); +} + +static struct cn_callback_entry *cn_queue_alloc_callback_entry(struct cn_callback *cb) +{ + struct cn_callback_entry *cbq; + + cbq = kmalloc(sizeof(*cbq), GFP_KERNEL); + if (!cbq) { + printk(KERN_ERR "Failed to create new callback queue.\n"); + return NULL; + } + + memset(cbq, 0, sizeof(*cbq)); + + cbq->cb = cb; + + INIT_WORK(&cbq->work, &cn_queue_wrapper, cbq); + + return cbq; +} + +static void cn_queue_free_callback(struct cn_callback_entry *cbq) +{ + cancel_delayed_work(&cbq->work); + + while (atomic_read(&cbq->cb->refcnt)) { + printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n", + cbq->pdev->name, atomic_read(&cbq->cb->refcnt)); + + msleep_interruptible(1000); + + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + + if (signal_pending(current)) + flush_signals(current); + } + + kfree(cbq); +} + +int cn_cb_equal(struct cb_id *i1, struct cb_id *i2) +{ +#if 0 + printk(KERN_INFO "%s: comparing %04x.%04x and %04x.%04x\n", + __func__, + i1->idx, i1->val, + i2->idx, i2->val); +#endif + return ((i1->idx == i2->idx) && (i1->val == i2->val)); +} + +int cn_queue_add_callback(struct cn_queue_dev *dev, struct cn_callback *cb) +{ + struct cn_callback_entry *cbq, *n, *__cbq; + int found = 0; + + cbq = cn_queue_alloc_callback_entry(cb); + if (!cbq) + return -ENOMEM; + + atomic_inc(&dev->refcnt); + smp_mb__after_atomic_inc(); + cbq->pdev = dev; + + spin_lock_bh(&dev->queue_lock); + list_for_each_entry_safe(__cbq, n, &dev->queue_list, callback_entry) { + if (cn_cb_equal(&__cbq->cb->id, &cb->id)) { + found = 1; + break; + } + } + if (!found) { + atomic_set(&cbq->cb->refcnt, 1); + list_add_tail(&cbq->callback_entry, &dev->queue_list); + } + spin_unlock_bh(&dev->queue_lock); + + if (found) { + smp_mb__before_atomic_inc(); + atomic_dec(&dev->refcnt); + smp_mb__after_atomic_inc(); + atomic_set(&cbq->cb->refcnt, 0); + cn_queue_free_callback(cbq); + return -EINVAL; + } + + cbq->nls = dev->nls; + cbq->seq = 0; + cbq->group = cbq->cb->id.idx; + + return 0; +} + +void cn_queue_del_callback(struct cn_queue_dev *dev, struct cn_callback *cb) +{ + struct cn_callback_entry *cbq = NULL, *n; + int found = 0; + + spin_lock_bh(&dev->queue_lock); + list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) { + if (cn_cb_equal(&cbq->cb->id, &cb->id)) { + list_del(&cbq->callback_entry); + found = 1; + break; + } + } + spin_unlock_bh(&dev->queue_lock); + + if (found) { + smp_mb__before_atomic_inc(); + atomic_dec(&cbq->cb->refcnt); + smp_mb__after_atomic_inc(); + cn_queue_free_callback(cbq); + smp_mb__before_atomic_inc(); + atomic_dec(&dev->refcnt); + smp_mb__after_atomic_inc(); + } +} + +struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *nls) +{ + struct cn_queue_dev *dev; + + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR "%s: Failed to allocte new struct cn_queue_dev.\n", + name); + return NULL; + } + + memset(dev, 0, sizeof(*dev)); + + snprintf(dev->name, sizeof(dev->name), "%s", name); + + atomic_set(&dev->refcnt, 0); + INIT_LIST_HEAD(&dev->queue_list); + spin_lock_init(&dev->queue_lock); + + dev->nls = nls; + dev->netlink_groups = 0; + + dev->cn_queue = create_workqueue(dev->name); + if (!dev->cn_queue) { + printk(KERN_ERR "Failed to create %s queue.\n", dev->name); + kfree(dev); + return NULL; + } + + return dev; +} + +void cn_queue_free_dev(struct cn_queue_dev *dev) +{ + struct cn_callback_entry *cbq, *n; + + flush_workqueue(dev->cn_queue); + destroy_workqueue(dev->cn_queue); + + spin_lock_bh(&dev->queue_lock); + list_for_each_entry_safe(cbq, n, &dev->queue_list, callback_entry) { + list_del(&cbq->callback_entry); + smp_mb__before_atomic_inc(); + atomic_dec(&cbq->cb->refcnt); + smp_mb__after_atomic_inc(); + } + spin_unlock_bh(&dev->queue_lock); + + while (atomic_read(&dev->refcnt)) { + printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n", + dev->name, atomic_read(&dev->refcnt)); + + msleep_interruptible(1000); + + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + + if (signal_pending(current)) + flush_signals(current); + } + + memset(dev, 0, sizeof(*dev)); + kfree(dev); + dev = NULL; +} diff -Nru a/drivers/connector/connector.c b/drivers/connector/connector.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/connector/connector.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,521 @@ +/* + * connector.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector."); + +static int unit = NETLINK_NFLOG; +static u32 cn_idx = -1; +static u32 cn_val = -1; + +module_param(unit, int, 0); +module_param(cn_idx, uint, 0); +module_param(cn_val, uint, 0); + +static DEFINE_SPINLOCK(notify_lock); +static LIST_HEAD(notify_list); + +static struct cn_dev cdev; + +int cn_already_initialized = 0; + +/* + * msg->seq and msg->ack are used to determine message genealogy. + * When someone sends message it puts there locally unique sequence + * and random acknowledge numbers. + * Sequence number may be copied into nlmsghdr->nlmsg_seq too. + * + * Sequence number is incremented with each message to be sent. + * + * If we expect reply to our message, + * then sequence number in received message MUST be the same as in original message, + * and acknowledge number MUST be the same + 1. + * + * If we receive message and it's sequence number is not equal to one we are expecting, + * then it is new message. + * If we receive message and it's sequence number is the same as one we are expecting, + * but it's acknowledge is not equal acknowledge number in original message + 1, + * then it is new message. + * + */ +void cn_netlink_send(struct cn_msg *msg, u32 __groups) +{ + struct cn_callback_entry *n, *__cbq; + unsigned int size; + struct sk_buff *skb, *uskb; + struct nlmsghdr *nlh; + struct cn_msg *data; + struct cn_dev *dev = &cdev; + u32 groups = 0; + int found = 0; + + if (!__groups) + { + spin_lock_bh(&dev->cbdev->queue_lock); + list_for_each_entry_safe(__cbq, n, &dev->cbdev->queue_list, callback_entry) { + if (cn_cb_equal(&__cbq->cb->id, &msg->id)) { + found = 1; + groups = __cbq->group; + } + } + spin_unlock_bh(&dev->cbdev->queue_lock); + + if (!found) { + printk(KERN_ERR "Failed to find multicast netlink group for callback[0x%x.0x%x]. seq=%u\n", + msg->id.idx, msg->id.val, msg->seq); + return; + } + } + else + groups = __groups; + + size = NLMSG_SPACE(sizeof(*msg) + msg->len); + + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) { + printk(KERN_ERR "Failed to allocate new skb with size=%u.\n", size); + return; + } + + nlh = NLMSG_PUT(skb, 0, msg->seq, NLMSG_DONE, size - sizeof(*nlh)); + + data = (struct cn_msg *)NLMSG_DATA(nlh); + + memcpy(data, msg, sizeof(*data) + msg->len); +#if 0 + printk("%s: len=%u, seq=%u, ack=%u, group=%u.\n", + __func__, msg->len, msg->seq, msg->ack, groups); +#endif + + NETLINK_CB(skb).dst_groups = groups; + + uskb = skb_clone(skb, GFP_ATOMIC); + if (uskb) { + netlink_unicast(dev->nls, uskb, 0, 0); + } + + netlink_broadcast(dev->nls, skb, 0, groups, GFP_ATOMIC); + + return; + +nlmsg_failure: + printk(KERN_ERR "Failed to send %u.%u\n", msg->seq, msg->ack); + kfree_skb(skb); + return; +} + +static int cn_call_callback(struct cn_msg *msg, void (*destruct_data) (void *), void *data) +{ + struct cn_callback_entry *n, *__cbq; + struct cn_dev *dev = &cdev; + int found = 0; + + spin_lock_bh(&dev->cbdev->queue_lock); + list_for_each_entry_safe(__cbq, n, &dev->cbdev->queue_list, callback_entry) { + if (cn_cb_equal(&__cbq->cb->id, &msg->id)) { + __cbq->cb->priv = msg; + + __cbq->ddata = data; + __cbq->destruct_data = destruct_data; + + queue_work(dev->cbdev->cn_queue, &__cbq->work); + found = 1; + break; + } + } + spin_unlock_bh(&dev->cbdev->queue_lock); + + return found; +} + +static int __cn_rx_skb(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + u32 pid, uid, seq, group; + struct cn_msg *msg; + + pid = NETLINK_CREDS(skb)->pid; + uid = NETLINK_CREDS(skb)->uid; + seq = nlh->nlmsg_seq; + group = NETLINK_CB((skb)).groups; + msg = (struct cn_msg *)NLMSG_DATA(nlh); + + if (msg->len != nlh->nlmsg_len - sizeof(*msg) - sizeof(*nlh)) { + printk(KERN_ERR "skb does not have enough length: " + "requested msg->len=%u[%u], nlh->nlmsg_len=%u[%u], skb->len=%u[must be %u].\n", + msg->len, NLMSG_SPACE(msg->len), + nlh->nlmsg_len, nlh->nlmsg_len - sizeof(*nlh), + skb->len, msg->len + sizeof(*msg)); + kfree_skb(skb); + return -EINVAL; + } +#if 0 + printk(KERN_INFO "pid=%u, uid=%u, seq=%u, group=%u.\n", + pid, uid, seq, group); +#endif + return cn_call_callback(msg, (void (*)(void *))kfree_skb, skb); +} + +static void cn_rx_skb(struct sk_buff *__skb) +{ + struct nlmsghdr *nlh; + u32 len; + int err; + struct sk_buff *skb; + + skb = skb_get(__skb); + if (!skb) { + printk(KERN_ERR "Failed to reference an skb.\n"); + kfree_skb(__skb); + return; + } +#if 0 + printk(KERN_INFO + "skb: len=%u, data_len=%u, truesize=%u, proto=%u, cloned=%d, shared=%d.\n", + skb->len, skb->data_len, skb->truesize, skb->protocol, + skb_cloned(skb), skb_shared(skb)); +#endif + while (skb->len >= NLMSG_SPACE(0)) { + nlh = (struct nlmsghdr *)skb->data; + if (nlh->nlmsg_len < sizeof(struct cn_msg) || + skb->len < nlh->nlmsg_len || + nlh->nlmsg_len > CONNECTOR_MAX_MSG_SIZE) { +#if 0 + printk(KERN_INFO "nlmsg_len=%u, sizeof(*nlh)=%u\n", + nlh->nlmsg_len, sizeof(*nlh)); +#endif + kfree_skb(skb); + break; + } + + len = NLMSG_ALIGN(nlh->nlmsg_len); + if (len > skb->len) + len = skb->len; + + err = __cn_rx_skb(skb, nlh); + if (err) { +#if 0 + if (err < 0 && (nlh->nlmsg_flags & NLM_F_ACK)) + netlink_ack(skb, nlh, -err); +#endif + break; + } else { +#if 0 + if (nlh->nlmsg_flags & NLM_F_ACK) + netlink_ack(skb, nlh, 0); +#endif + break; + } + skb_pull(skb, len); + } + + kfree_skb(__skb); +} + +static void cn_input(struct sock *sk, int len) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) + cn_rx_skb(skb); +} + +static void cn_notify(struct cb_id *id, u32 notify_event) +{ + struct cn_ctl_entry *ent; + + spin_lock_bh(¬ify_lock); + list_for_each_entry(ent, ¬ify_list, notify_entry) { + int i; + struct cn_notify_req *req; + struct cn_ctl_msg *ctl = ent->msg; + int a, b; + + a = b = 0; + + req = (struct cn_notify_req *)ctl->data; + for (i=0; iidx_notify_num; ++i, ++req) { + if (id->idx >= req->first && id->idx < req->first + req->range) { + a = 1; + break; + } + } + + for (i=0; ival_notify_num; ++i, ++req) { + if (id->val >= req->first && id->val < req->first + req->range) { + b = 1; + break; + } + } + + if (a && b) { + struct cn_msg m; + + printk(KERN_INFO "Notifying group %x with event %u about %x.%x.\n", + ctl->group, notify_event, + id->idx, id->val); + + memset(&m, 0, sizeof(m)); + m.ack = notify_event; + + memcpy(&m.id, id, sizeof(m.id)); + cn_netlink_send(&m, ctl->group); + } + } + spin_unlock_bh(¬ify_lock); +} + +int cn_add_callback(struct cb_id *id, char *name, void (*callback) (void *)) +{ + int err; + struct cn_dev *dev = &cdev; + struct cn_callback *cb; + + cb = kmalloc(sizeof(*cb), GFP_KERNEL); + if (!cb) { + printk(KERN_INFO "%s: Failed to allocate new struct cn_callback.\n", + dev->cbdev->name); + return -ENOMEM; + } + + memset(cb, 0, sizeof(*cb)); + + snprintf(cb->name, sizeof(cb->name), "%s", name); + + memcpy(&cb->id, id, sizeof(cb->id)); + cb->callback = callback; + + atomic_set(&cb->refcnt, 0); + + err = cn_queue_add_callback(dev->cbdev, cb); + if (err) { + kfree(cb); + return err; + } + + cn_notify(id, 0); + + return 0; +} + +void cn_del_callback(struct cb_id *id) +{ + struct cn_dev *dev = &cdev; + struct cn_callback_entry *n, *__cbq; + + list_for_each_entry_safe(__cbq, n, &dev->cbdev->queue_list, callback_entry) { + if (cn_cb_equal(&__cbq->cb->id, id)) { + cn_queue_del_callback(dev->cbdev, __cbq->cb); + cn_notify(id, 1); + break; + } + } +} + +static int cn_ctl_msg_equals(struct cn_ctl_msg *m1, struct cn_ctl_msg *m2) +{ + int i; + struct cn_notify_req *req1, *req2; + + if (m1->idx_notify_num != m2->idx_notify_num) + return 0; + + if (m1->val_notify_num != m2->val_notify_num) + return 0; + + if (m1->len != m2->len) + return 0; + + if ((m1->idx_notify_num + m1->val_notify_num)*sizeof(*req1) != m1->len) { + printk(KERN_ERR "Notify entry[idx_num=%x, val_num=%x, len=%u] contains garbage. Removing.\n", + m1->idx_notify_num, m1->val_notify_num, m1->len); + return 1; + } + + req1 = (struct cn_notify_req *)m1->data; + req2 = (struct cn_notify_req *)m2->data; + + for (i=0; iidx_notify_num; ++i) { + if (memcmp(req1, req2, sizeof(*req1))) + return 0; + + req1++; + req2++; + } + + for (i=0; ival_notify_num; ++i) { + if (memcmp(req1, req2, sizeof(*req1))) + return 0; + + req1++; + req2++; + } + + return 1; +} + +static void cn_callback(void * data) +{ + struct cn_msg *msg = (struct cn_msg *)data; + struct cn_ctl_msg *ctl; + struct cn_ctl_entry *ent; + u32 size; + + if (msg->len < sizeof(*ctl)) { + printk(KERN_ERR "Wrong connector request size %u, must be >= %u.\n", + msg->len, sizeof(*ctl)); + return; + } + + ctl = (struct cn_ctl_msg *)msg->data; + + size = sizeof(*ctl) + (ctl->idx_notify_num + ctl->val_notify_num)*sizeof(struct cn_notify_req); + + if (msg->len != size) { + printk(KERN_ERR "Wrong connector request size %u, must be == %u.\n", + msg->len, size); + return; + } + + if (ctl->len + sizeof(*ctl) != msg->len) { + printk(KERN_ERR "Wrong message: msg->len=%u must be equal to inner_len=%u [+%u].\n", + msg->len, ctl->len, sizeof(*ctl)); + return; + } + + /* + * Remove notification. + */ + if (ctl->group == 0) { + struct cn_ctl_entry *n; + + spin_lock_bh(¬ify_lock); + list_for_each_entry_safe(ent, n, ¬ify_list, notify_entry) { + if (cn_ctl_msg_equals(ent->msg, ctl)) { + list_del(&ent->notify_entry); + kfree(ent); + } + } + spin_unlock_bh(¬ify_lock); + + return; + } + + size += sizeof(*ent); + + ent = kmalloc(size, GFP_ATOMIC); + if (!ent) { + printk(KERN_ERR "Failed to allocate %d bytes for new notify entry.\n", size); + return; + } + + memset(ent, 0, size); + + ent->msg = (struct cn_ctl_msg *)(ent + 1); + + memcpy(ent->msg, ctl, size - sizeof(*ent)); + + spin_lock_bh(¬ify_lock); + list_add(&ent->notify_entry, ¬ify_list); + spin_unlock_bh(¬ify_lock); + + { + int i; + struct cn_notify_req *req; + + printk("Notify group %x for idx: ", ctl->group); + + req = (struct cn_notify_req *)ctl->data; + for (i=0; iidx_notify_num; ++i, ++req) { + printk("%u-%u ", req->first, req->first+req->range-1); + } + + printk("\nNotify group %x for val: ", ctl->group); + + for (i=0; ival_notify_num; ++i, ++req) { + printk("%u-%u ", req->first, req->first+req->range-1); + } + printk("\n"); + } +} + +static int cn_init(void) +{ + struct cn_dev *dev = &cdev; + int err; + + dev->input = cn_input; + dev->id.idx = cn_idx; + dev->id.val = cn_val; + + dev->nls = netlink_kernel_create(unit, dev->input); + if (!dev->nls) { + printk(KERN_ERR "Failed to create new netlink socket(%u).\n", + unit); + return -EIO; + } + + dev->cbdev = cn_queue_alloc_dev("cqueue", dev->nls); + if (!dev->cbdev) { + if (dev->nls->sk_socket) + sock_release(dev->nls->sk_socket); + return -EINVAL; + } + + err = cn_add_callback(&dev->id, "connector", &cn_callback); + if (err) { + cn_queue_free_dev(dev->cbdev); + if (dev->nls->sk_socket) + sock_release(dev->nls->sk_socket); + return -EINVAL; + } + + cn_already_initialized = 1; + + return 0; +} + +static void cn_fini(void) +{ + struct cn_dev *dev = &cdev; + + cn_del_callback(&dev->id); + cn_queue_free_dev(dev->cbdev); + if (dev->nls->sk_socket) + sock_release(dev->nls->sk_socket); +} + +module_init(cn_init); +module_exit(cn_fini); + +EXPORT_SYMBOL_GPL(cn_add_callback); +EXPORT_SYMBOL_GPL(cn_del_callback); +EXPORT_SYMBOL_GPL(cn_netlink_send); diff -Nru a/drivers/i2c/algos/i2c-algo-ite.c b/drivers/i2c/algos/i2c-algo-ite.c --- a/drivers/i2c/algos/i2c-algo-ite.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/algos/i2c-algo-ite.c 2005-03-23 19:38:41 -08:00 @@ -713,14 +713,11 @@ /* -----exported algorithm data: ------------------------------------- */ static struct i2c_algorithm iic_algo = { - "ITE IIC algorithm", - I2C_ALGO_IIC, - iic_xfer, /* master_xfer */ - NULL, /* smbus_xfer */ - NULL, /* slave_xmit */ - NULL, /* slave_recv */ - algo_control, /* ioctl */ - iic_func, /* functionality */ + .name = "ITE IIC algorithm", + .id = I2C_ALGO_IIC, + .master_xfer = iic_xfer, + .algo_control = algo_control, /* ioctl */ + .functionality = iic_func, }; diff -Nru a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c --- a/drivers/i2c/algos/i2c-algo-pcf.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/algos/i2c-algo-pcf.c 2005-03-23 19:38:41 -08:00 @@ -78,7 +78,6 @@ set_pcf(adap, 1, I2C_PCF_STOP); } - static int wait_for_bb(struct i2c_algo_pcf_data *adap) { int timeout = DEF_TIMEOUT; @@ -109,6 +108,26 @@ adap->waitforpin(); *status = get_pcf(adap, 1); } + if (*status & I2C_PCF_LAB) { + DEB2(printk(KERN_INFO + "i2c-algo-pcf.o: lost arbitration (CSR 0x%02x)\n", + *status)); + /* Cleanup from LAB-- reset and enable ESO. + * This resets the PCF8584; since we've lost the bus, no + * further attempts should be made by callers to clean up + * (no i2c_stop() etc.) + */ + set_pcf(adap, 1, I2C_PCF_PIN); + set_pcf(adap, 1, I2C_PCF_ESO); + /* TODO: we should pause for a time period sufficient for any + * running I2C transaction to complete-- the arbitration + * logic won't work properly until the next START is seen. + */ + DEB2(printk(KERN_INFO + "i2c-algo-pcf.o: reset LAB condition (CSR 0x%02x)\n", + get_pcf(adap,1))); + return(-EINTR); + } #endif if (timeout <= 0) return(-1); @@ -188,16 +207,22 @@ unsigned char addr, int retries) { int i, status, ret = -1; + int wfp; for (i=0;i= 0) { + if ((wfp = wait_for_pin(adap, &status)) >= 0) { if ((status & I2C_PCF_LRB) == 0) { i2c_stop(adap); break; /* success! */ } } + if (wfp == -EINTR) { + /* arbitration lost */ + udelay(adap->udelay); + return -EINTR; + } i2c_stop(adap); udelay(adap->udelay); } @@ -219,6 +244,10 @@ i2c_outb(adap, buf[wrcount]); timeout = wait_for_pin(adap, &status); if (timeout) { + if (timeout == -EINTR) { + /* arbitration lost */ + return -EINTR; + } i2c_stop(adap); dev_err(&i2c_adap->dev, "i2c_write: error - timeout.\n"); return -EREMOTEIO; /* got a better one ?? */ @@ -247,11 +276,16 @@ { int i, status; struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wfp; /* increment number of bytes to read by one -- read dummy byte */ for (i = 0; i <= count; i++) { - if (wait_for_pin(adap, &status)) { + if ((wfp = wait_for_pin(adap, &status))) { + if (wfp == -EINTR) { + /* arbitration lost */ + return -EINTR; + } i2c_stop(adap); dev_err(&i2c_adap->dev, "pcf_readbytes timed out.\n"); return (-1); @@ -366,6 +400,10 @@ /* Wait for PIN (pending interrupt NOT) */ timeout = wait_for_pin(adap, &status); if (timeout) { + if (timeout == -EINTR) { + /* arbitration lost */ + return (-EINTR); + } i2c_stop(adap); DEB2(printk(KERN_ERR "i2c-algo-pcf.o: Timeout waiting " "for PIN(1) in pcf_xfer\n");) diff -Nru a/drivers/i2c/algos/i2c-algo-sibyte.c b/drivers/i2c/algos/i2c-algo-sibyte.c --- a/drivers/i2c/algos/i2c-algo-sibyte.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/algos/i2c-algo-sibyte.c 2005-03-23 19:38:41 -08:00 @@ -136,14 +136,11 @@ /* -----exported algorithm data: ------------------------------------- */ static struct i2c_algorithm i2c_sibyte_algo = { - "SiByte algorithm", - I2C_ALGO_SIBYTE, - NULL, /* master_xfer */ - smbus_xfer, /* smbus_xfer */ - NULL, /* slave_xmit */ - NULL, /* slave_recv */ - algo_control, /* ioctl */ - bit_func, /* functionality */ + .name = "SiByte algorithm", + .id = I2C_ALGO_SIBYTE, + .smbus_xfer = smbus_xfer, + .algo_control = algo_control, /* ioctl */ + .functionality = bit_func, }; /* diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig --- a/drivers/i2c/busses/Kconfig 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/busses/Kconfig 2005-03-23 19:38:41 -08:00 @@ -108,7 +108,7 @@ will be called i2c-hydra. config I2C_I801 - tristate "Intel 801" + tristate "Intel 82801 (ICH)" depends on I2C && PCI && EXPERIMENTAL help If you say yes to this option, support will be included for the Intel @@ -119,7 +119,7 @@ 82801BA 82801CA/CAM 82801DB - 82801EB + 82801EB/ER (ICH5/ICH5R) 6300ESB ICH6 ICH7 @@ -143,6 +143,23 @@ This driver can also be built as a module. If so, the module will be called i2c-i810. +config I2C_PIIX4 + tristate "Intel PIIX4" + depends on I2C && PCI + help + If you say yes to this option, support will be included for the Intel + PIIX4 family of mainboard I2C interfaces. Specifically, the following + versions of the chipset are supported: + Intel PIIX4 + Intel 440MX + Serverworks OSB4 + Serverworks CSB5 + Serverworks CSB6 + SMSC Victory66 + + This driver can also be built as a module. If so, the module + will be called i2c-piix4. + config I2C_IBM_IIC tristate "IBM PPC 4xx on-chip I2C interface" depends on IBM_OCP && I2C @@ -284,23 +301,6 @@ This support is also available as a module. If so, the module will be called i2c-parport-light. - -config I2C_PIIX4 - tristate "Intel PIIX4" - depends on I2C && PCI && EXPERIMENTAL - help - If you say yes to this option, support will be included for the Intel - PIIX4 family of mainboard I2C interfaces. Specifically, the following - versions of the chipset are supported: - Intel PIIX4 - Intel 440MX - Serverworks OSB4 - Serverworks CSB5 - Serverworks CSB6 - SMSC Victory66 - - This driver can also be built as a module. If so, the module - will be called i2c-piix4. config I2C_PROSAVAGE tristate "S3/VIA (Pro)Savage" diff -Nru a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c --- a/drivers/i2c/busses/i2c-elektor.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/busses/i2c-elektor.c 2005-03-23 19:38:41 -08:00 @@ -110,22 +110,23 @@ } static void pcf_isa_waitforpin(void) { - + DEFINE_WAIT(wait); int timeout = 2; - long flags; + unsigned long flags; if (irq > 0) { spin_lock_irqsave(&lock, flags); if (pcf_pending == 0) { spin_unlock_irqrestore(&lock, flags); - if (interruptible_sleep_on_timeout(&pcf_wait, - timeout*HZ)) { + prepare_to_wait(&pcf_wait, &wait, TASK_INTERRUPTIBLE); + if (schedule_timeout(timeout*HZ)) { spin_lock_irqsave(&lock, flags); if (pcf_pending == 1) { pcf_pending = 0; } spin_unlock_irqrestore(&lock, flags); } + finish_wait(&pcf_wait, &wait); } else { pcf_pending = 0; spin_unlock_irqrestore(&lock, flags); diff -Nru a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c --- a/drivers/i2c/busses/i2c-ibm_iic.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/busses/i2c-ibm_iic.c 2005-03-23 19:38:41 -08:00 @@ -630,10 +630,6 @@ .name = "IBM IIC algorithm", .id = I2C_ALGO_OCP, .master_xfer = iic_xfer, - .smbus_xfer = NULL, - .slave_send = NULL, - .slave_recv = NULL, - .algo_control = NULL, .functionality = iic_func }; diff -Nru a/drivers/i2c/busses/i2c-ite.c b/drivers/i2c/busses/i2c-ite.c --- a/drivers/i2c/busses/i2c-ite.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/busses/i2c-ite.c 2005-03-23 19:38:41 -08:00 @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -107,7 +108,7 @@ * IIC controller interrupts. */ static void iic_ite_waitforpin(void) { - + DEFINE_WAIT(wait); int timeout = 2; long flags; @@ -121,13 +122,15 @@ spin_lock_irqsave(&lock, flags); if (iic_pending == 0) { spin_unlock_irqrestore(&lock, flags); - if (interruptible_sleep_on_timeout(&iic_wait, timeout*HZ)) { + prepare_to_wait(&iic_wait, &wait, TASK_INTERRUPTIBLE); + if (schedule_timeout(timeout*HZ)) { spin_lock_irqsave(&lock, flags); if (iic_pending == 1) { iic_pending = 0; } spin_unlock_irqrestore(&lock, flags); } + finish_wait(&iic_wait, &wait); } else { iic_pending = 0; spin_unlock_irqrestore(&lock, flags); diff -Nru a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c --- a/drivers/i2c/busses/i2c-viapro.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/busses/i2c-viapro.c 2005-03-23 19:38:41 -08:00 @@ -121,12 +121,12 @@ inb_p(SMBHSTDAT1)); /* Make sure the SMBus host is ready to start transmitting */ - if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { dev_dbg(&vt596_adapter.dev, "SMBus busy (0x%02x). " "Resetting...\n", temp); outb_p(temp, SMBHSTSTS); - if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { dev_dbg(&vt596_adapter.dev, "Failed! (0x%02x)\n", temp); return -1; @@ -168,13 +168,14 @@ dev_dbg(&vt596_adapter.dev, "Error: no response!\n"); } - if (inb_p(SMBHSTSTS) != 0x00) - outb_p(inb(SMBHSTSTS), SMBHSTSTS); - - if ((temp = inb_p(SMBHSTSTS)) != 0x00) { - dev_dbg(&vt596_adapter.dev, "Failed reset at end of " - "transaction (%02x)\n", temp); + if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & 0x1F) { + dev_warn(&vt596_adapter.dev, "Failed reset at end " + "of transaction (%02x)\n", temp); + } } + dev_dbg(&vt596_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, " "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig --- a/drivers/i2c/chips/Kconfig 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/Kconfig 2005-03-23 19:38:41 -08:00 @@ -236,6 +236,17 @@ This driver can also be built as a module. If so, the module will be called lm90. +config SENSORS_LM92 + tristate "National Semiconductor LM92 and compatibles" + depends on I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor LM92 + and Maxim MAX6635 sensor chips. + + This driver can also be built as a module. If so, the module + will be called lm92. + config SENSORS_MAX1619 tristate "Maxim MAX1619 sensor chip" depends on I2C && EXPERIMENTAL @@ -350,6 +361,17 @@ menu "Other I2C Chip support" depends on I2C + +config SENSORS_DS1337 + tristate "Dallas Semiconductor DS1337 Real Time Clock" + depends on I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Dallas Semiconductor + DS1337 real-time clock chips. + + This driver can also be built as a module. If so, the module + will be called ds1337. config SENSORS_EEPROM tristate "EEPROM reader" diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile --- a/drivers/i2c/chips/Makefile 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/Makefile 2005-03-23 19:38:41 -08:00 @@ -11,6 +11,7 @@ obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o +obj-$(CONFIG_SENSORS_DS1337) += ds1337.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o obj-$(CONFIG_SENSORS_FSCHER) += fscher.o @@ -27,6 +28,7 @@ obj-$(CONFIG_SENSORS_LM85) += lm85.o obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM90) += lm90.o +obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_M41T00) += m41t00.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o diff -Nru a/drivers/i2c/chips/adm1021.c b/drivers/i2c/chips/adm1021.c --- a/drivers/i2c/chips/adm1021.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/adm1021.c 2005-03-23 19:38:41 -08:00 @@ -28,18 +28,6 @@ #include -/* Registers */ -#define ADM1021_SYSCTL_TEMP 1200 -#define ADM1021_SYSCTL_REMOTE_TEMP 1201 -#define ADM1021_SYSCTL_DIE_CODE 1202 -#define ADM1021_SYSCTL_ALARMS 1203 - -#define ADM1021_ALARM_TEMP_HIGH 0x40 -#define ADM1021_ALARM_TEMP_LOW 0x20 -#define ADM1021_ALARM_RTEMP_HIGH 0x10 -#define ADM1021_ALARM_RTEMP_LOW 0x08 -#define ADM1021_ALARM_RTEMP_NA 0x04 - /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, @@ -380,7 +368,7 @@ data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP); data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R); data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R); - data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec; + data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0x7c; if (data->type == adm1021) data->die_code = adm1021_read_value(client, ADM1021_REG_DIE_CODE); if (data->type == adm1023) { diff -Nru a/drivers/i2c/chips/ds1337.c b/drivers/i2c/chips/ds1337.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/i2c/chips/ds1337.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,402 @@ +/* + * linux/drivers/i2c/chips/ds1337.c + * + * Copyright (C) 2005 James Chapman + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Driver for Dallas Semiconductor DS1337 real time clock chip + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* get the user-level API */ +#include +#include + +/* Device registers */ +#define DS1337_REG_HOUR 2 +#define DS1337_REG_DAY 3 +#define DS1337_REG_DATE 4 +#define DS1337_REG_MONTH 5 +#define DS1337_REG_CONTROL 14 +#define DS1337_REG_STATUS 15 + +/* FIXME - how do we export these interface constants? */ +#define DS1337_GET_DATE 0 +#define DS1337_SET_DATE 1 + +/* + * Functions declaration + */ +static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +SENSORS_INSMOD_1(ds1337); + +static int ds1337_attach_adapter(struct i2c_adapter *adapter); +static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind); +static void ds1337_init_client(struct i2c_client *client); +static int ds1337_detach_client(struct i2c_client *client); +static int ds1337_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +/* + * Driver data (common to all clients) + */ +static struct i2c_driver ds1337_driver = { + .owner = THIS_MODULE, + .name = "ds1337", + .flags = I2C_DF_NOTIFY, + .attach_adapter = ds1337_attach_adapter, + .detach_client = ds1337_detach_client, + .command = ds1337_command, +}; + +/* + * Client data (each client gets its own) + */ +struct ds1337_data { + struct i2c_client client; + struct list_head list; + int id; +}; + +/* + * Internal variables + */ +static int ds1337_id; +static LIST_HEAD(ds1337_clients); + +static inline int ds1337_read(struct i2c_client *client, u8 reg, u8 *value) +{ + s32 tmp = i2c_smbus_read_byte_data(client, reg); + + if (tmp < 0) + return -EIO; + + *value = tmp; + + return 0; +} + +/* + * Chip access functions + */ +static int ds1337_get_datetime(struct i2c_client *client, struct rtc_time *dt) +{ + struct ds1337_data *data = i2c_get_clientdata(client); + int result; + u8 buf[7]; + u8 val; + struct i2c_msg msg[2]; + u8 offs = 0; + + if (!dt) { + dev_dbg(&client->adapter->dev, "%s: EINVAL: dt=NULL\n", + __FUNCTION__); + + return -EINVAL; + } + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &offs; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = sizeof(buf); + msg[1].buf = &buf[0]; + + result = client->adapter->algo->master_xfer(client->adapter, + &msg[0], 2); + + dev_dbg(&client->adapter->dev, + "%s: [%d] %02x %02x %02x %02x %02x %02x %02x\n", + __FUNCTION__, result, buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6]); + + if (result >= 0) { + dt->tm_sec = BCD_TO_BIN(buf[0]); + dt->tm_min = BCD_TO_BIN(buf[1]); + val = buf[2] & 0x3f; + dt->tm_hour = BCD_TO_BIN(val); + dt->tm_wday = BCD_TO_BIN(buf[3]) - 1; + dt->tm_mday = BCD_TO_BIN(buf[4]); + val = buf[5] & 0x7f; + dt->tm_mon = BCD_TO_BIN(val); + dt->tm_year = 1900 + BCD_TO_BIN(buf[6]); + if (buf[5] & 0x80) + dt->tm_year += 100; + + dev_dbg(&client->adapter->dev, "%s: secs=%d, mins=%d, " + "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", + __FUNCTION__, dt->tm_sec, dt->tm_min, + dt->tm_hour, dt->tm_mday, + dt->tm_mon, dt->tm_year, dt->tm_wday); + } else { + dev_err(&client->adapter->dev, "ds1337[%d]: error reading " + "data! %d\n", data->id, result); + result = -EIO; + } + + return result; +} + +static int ds1337_set_datetime(struct i2c_client *client, struct rtc_time *dt) +{ + struct ds1337_data *data = i2c_get_clientdata(client); + int result; + u8 buf[8]; + u8 val; + struct i2c_msg msg[1]; + + if (!dt) { + dev_dbg(&client->adapter->dev, "%s: EINVAL: dt=NULL\n", + __FUNCTION__); + + return -EINVAL; + } + + dev_dbg(&client->adapter->dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", __FUNCTION__, + dt->tm_sec, dt->tm_min, dt->tm_hour, + dt->tm_mday, dt->tm_mon, dt->tm_year, dt->tm_wday); + + buf[0] = 0; /* reg offset */ + buf[1] = BIN_TO_BCD(dt->tm_sec); + buf[2] = BIN_TO_BCD(dt->tm_min); + buf[3] = BIN_TO_BCD(dt->tm_hour) | (1 << 6); + buf[4] = BIN_TO_BCD(dt->tm_wday) + 1; + buf[5] = BIN_TO_BCD(dt->tm_mday); + buf[6] = BIN_TO_BCD(dt->tm_mon); + if (dt->tm_year >= 2000) { + val = dt->tm_year - 2000; + buf[6] |= (1 << 7); + } else { + val = dt->tm_year - 1900; + } + buf[7] = BIN_TO_BCD(val); + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = sizeof(buf); + msg[0].buf = &buf[0]; + + result = client->adapter->algo->master_xfer(client->adapter, + &msg[0], 1); + if (result < 0) { + dev_err(&client->adapter->dev, "ds1337[%d]: error " + "writing data! %d\n", data->id, result); + result = -EIO; + } else { + result = 0; + } + + return result; +} + +static int ds1337_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + dev_dbg(&client->adapter->dev, "%s: cmd=%d\n", __FUNCTION__, cmd); + + switch (cmd) { + case DS1337_GET_DATE: + return ds1337_get_datetime(client, arg); + + case DS1337_SET_DATE: + return ds1337_set_datetime(client, arg); + + default: + return -EINVAL; + } +} + +/* + * Public API for access to specific device. Useful for low-level + * RTC access from kernel code. + */ +int ds1337_do_command(int id, int cmd, void *arg) +{ + struct list_head *walk; + struct list_head *tmp; + struct ds1337_data *data; + + list_for_each_safe(walk, tmp, &ds1337_clients) { + data = list_entry(walk, struct ds1337_data, list); + if (data->id == id) + return ds1337_command(&data->client, cmd, arg); + } + + return -ENODEV; +} + +static int ds1337_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, ds1337_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int ds1337_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct ds1337_data *data; + int err = 0; + const char *name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_I2C)) + goto exit; + + if (!(data = kmalloc(sizeof(struct ds1337_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct ds1337_data)); + INIT_LIST_HEAD(&data->list); + + /* The common I2C client data is placed right before the + * DS1337-specific data. + */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &ds1337_driver; + new_client->flags = 0; + + /* + * Now we do the remaining detection. A negative kind means that + * the driver was loaded with no force parameter (default), so we + * must both detect and identify the chip. A zero kind means that + * the driver was loaded with the force parameter, the detection + * step shall be skipped. A positive kind means that the driver + * was loaded with the force parameter and a given kind of chip is + * requested, so both the detection and the identification steps + * are skipped. + * + * For detection, we read registers that are most likely to cause + * detection failure, i.e. those that have more bits with fixed + * or reserved values. + */ + + /* Default to an DS1337 if forced */ + if (kind == 0) + kind = ds1337; + + if (kind < 0) { /* detection and identification */ + u8 data; + + /* Check that status register bits 6-2 are zero */ + if ((ds1337_read(new_client, DS1337_REG_STATUS, &data) < 0) || + (data & 0x7c)) + goto exit_free; + + /* Check for a valid day register value */ + if ((ds1337_read(new_client, DS1337_REG_DAY, &data) < 0) || + (data == 0) || (data & 0xf8)) + goto exit_free; + + /* Check for a valid date register value */ + if ((ds1337_read(new_client, DS1337_REG_DATE, &data) < 0) || + (data == 0) || (data & 0xc0) || ((data & 0x0f) > 9) || + (data >= 0x32)) + goto exit_free; + + /* Check for a valid month register value */ + if ((ds1337_read(new_client, DS1337_REG_MONTH, &data) < 0) || + (data == 0) || (data & 0x60) || ((data & 0x0f) > 9) || + ((data >= 0x13) && (data <= 0x19))) + goto exit_free; + + /* Check that control register bits 6-5 are zero */ + if ((ds1337_read(new_client, DS1337_REG_CONTROL, &data) < 0) || + (data & 0x60)) + goto exit_free; + + kind = ds1337; + } + + if (kind == ds1337) + name = "ds1337"; + + /* We can fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the DS1337 chip */ + ds1337_init_client(new_client); + + /* Add client to local list */ + data->id = ds1337_id++; + list_add(&data->list, &ds1337_clients); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static void ds1337_init_client(struct i2c_client *client) +{ + s32 val; + + /* Ensure that device is set in 24-hour mode */ + val = i2c_smbus_read_byte_data(client, DS1337_REG_HOUR); + if ((val >= 0) && (val & (1 << 6)) == 0) + i2c_smbus_write_byte_data(client, DS1337_REG_HOUR, + val | (1 << 6)); +} + +static int ds1337_detach_client(struct i2c_client *client) +{ + int err; + struct ds1337_data *data = i2c_get_clientdata(client); + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + list_del(&data->list); + kfree(data); + return 0; +} + +static int __init ds1337_init(void) +{ + return i2c_add_driver(&ds1337_driver); +} + +static void __exit ds1337_exit(void) +{ + i2c_del_driver(&ds1337_driver); +} + +MODULE_AUTHOR("James Chapman "); +MODULE_DESCRIPTION("DS1337 RTC driver"); +MODULE_LICENSE("GPL"); + +module_init(ds1337_init); +module_exit(ds1337_exit); diff -Nru a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c --- a/drivers/i2c/chips/eeprom.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/eeprom.c 2005-03-23 19:38:41 -08:00 @@ -210,10 +210,11 @@ if (i2c_smbus_read_byte_data(new_client, 0x80) == 'P' && i2c_smbus_read_byte(new_client) == 'C' && i2c_smbus_read_byte(new_client) == 'G' - && i2c_smbus_read_byte(new_client) == '-') + && i2c_smbus_read_byte(new_client) == '-') { dev_info(&new_client->dev, "Vaio EEPROM detected, " "enabling password protection\n"); data->nature = VAIO; + } } /* create the sysfs eeprom file */ diff -Nru a/drivers/i2c/chips/it87.c b/drivers/i2c/chips/it87.c --- a/drivers/i2c/chips/it87.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/it87.c 2005-03-23 19:38:41 -08:00 @@ -734,10 +734,9 @@ goto ERROR0; /* Probe whether there is anything available on this address. Already - done for SMBus clients */ + done for SMBus and Super-I/O clients */ if (kind < 0) { - if (is_isa) { - + if (is_isa && !chip_type) { #define REALLY_SLOW_IO /* We need the timeouts for at least some IT87-like chips. But only if we read 'undefined' registers. */ diff -Nru a/drivers/i2c/chips/lm85.c b/drivers/i2c/chips/lm85.c --- a/drivers/i2c/chips/lm85.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/lm85.c 2005-03-23 19:38:41 -08:00 @@ -37,7 +37,7 @@ static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; /* Insmod parameters */ -SENSORS_INSMOD_5(lm85b, lm85c, adm1027, adt7463, emc6d100); +SENSORS_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); /* The LM85 registers */ @@ -74,8 +74,10 @@ #define LM85_VERSTEP_LM85B 0x62 #define LM85_VERSTEP_ADM1027 0x60 #define LM85_VERSTEP_ADT7463 0x62 +#define LM85_VERSTEP_ADT7463C 0x6A #define LM85_VERSTEP_EMC6D100_A0 0x60 #define LM85_VERSTEP_EMC6D100_A1 0x61 +#define LM85_VERSTEP_EMC6D102 0x65 #define LM85_REG_CONFIG 0x40 @@ -112,9 +114,13 @@ #define EMC6D100_REG_ALARM3 0x7d /* IN5, IN6 and IN7 */ -#define EMC6D100_REG_IN(nr) (0x70 + ((nr)-5)) -#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2) -#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2) +#define EMC6D100_REG_IN(nr) (0x70 + ((nr)-5)) +#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2) +#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2) +#define EMC6D102_REG_EXTEND_ADC1 0x85 +#define EMC6D102_REG_EXTEND_ADC2 0x86 +#define EMC6D102_REG_EXTEND_ADC3 0x87 +#define EMC6D102_REG_EXTEND_ADC4 0x88 #define LM85_ALARM_IN0 0x0001 #define LM85_ALARM_IN1 0x0002 @@ -139,35 +145,36 @@ these macros are called: arguments may be evaluated more than once. */ -/* IN are scaled 1.000 == 0xc0, mag = 3 */ -#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*0xc0+500)/1000),0,255)) -#define INEXT_FROM_REG(val,ext) (((val)*1000 + (ext)*250 + 96)/0xc0) -#define IN_FROM_REG(val) (INEXT_FROM_REG(val,0)) - /* IN are scaled acording to built-in resistors */ static int lm85_scaling[] = { /* .001 Volts */ 2500, 2250, 3300, 5000, 12000, 3300, 1500, 1800 /*EMC6D100*/ }; #define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from)) -#define INS_TO_REG(n,val) (SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255)) -#define INSEXT_FROM_REG(n,val,ext) (SCALE((val)*4 + (ext),192*4,lm85_scaling[n])) -#define INS_FROM_REG(n,val) (INSEXT_FROM_REG(n,val,0)) + +#define INS_TO_REG(n,val) \ + SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255) + +#define INSEXT_FROM_REG(n,val,ext,scale) \ + SCALE((val)*(scale) + (ext),192*(scale),lm85_scaling[n]) + +#define INS_FROM_REG(n,val) INSEXT_FROM_REG(n,val,0,1) /* FAN speed is measured using 90kHz clock */ #define FAN_TO_REG(val) (SENSORS_LIMIT( (val)<=0?0: 5400000/(val),0,65534)) #define FAN_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:5400000/(val)) /* Temperature is reported in .001 degC increments */ -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+500)/1000,-127,127)) -#define TEMPEXT_FROM_REG(val,ext) ((val)*1000 + (ext)*250) -#define TEMP_FROM_REG(val) (TEMPEXT_FROM_REG(val,0)) -#define EXTTEMP_TO_REG(val) (SENSORS_LIMIT((val)/250,-127,127)) +#define TEMP_TO_REG(val) \ + SENSORS_LIMIT(SCALE(val,1000,1),-127,127) +#define TEMPEXT_FROM_REG(val,ext,scale) \ + SCALE((val)*scale + (ext),scale,1000) +#define TEMP_FROM_REG(val) \ + TEMPEXT_FROM_REG(val,0,1) #define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255)) #define PWM_FROM_REG(val) (val) -#define EXT_FROM_REG(val,sensor) (((val)>>(sensor * 2))&0x03) /* ZONEs have the following parameters: * Limit (low) temp, 1. degC @@ -355,7 +362,9 @@ u8 pwm[3]; /* Register value */ u8 spinup_ctl; /* Register encoding, combined */ u8 tach_mode; /* Register encoding, combined */ - u16 extend_adc; /* Register value */ + u8 temp_ext[3]; /* Decoded values */ + u8 in_ext[8]; /* Decoded values */ + u8 adc_scale; /* ADC Extended bits scaling factor */ u8 fan_ppr; /* Register value */ u8 smooth[3]; /* Register encoding */ u8 vid; /* Register value */ @@ -536,7 +545,10 @@ static ssize_t show_in(struct device *dev, char *buf, int nr) { struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in[nr]) ); + return sprintf( buf, "%d\n", INSEXT_FROM_REG(nr, + data->in[nr], + data->in_ext[nr], + data->adc_scale) ); } static ssize_t show_in_min(struct device *dev, char *buf, int nr) { @@ -617,7 +629,9 @@ static ssize_t show_temp(struct device *dev, char *buf, int nr) { struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp[nr]) ); + return sprintf(buf,"%d\n", TEMPEXT_FROM_REG(data->temp[nr], + data->temp_ext[nr], + data->adc_scale) ); } static ssize_t show_temp_min(struct device *dev, char *buf, int nr) { @@ -1089,7 +1103,8 @@ && verstep == LM85_VERSTEP_ADM1027 ) { kind = adm1027 ; } else if( company == LM85_COMPANY_ANALOG_DEV - && verstep == LM85_VERSTEP_ADT7463 ) { + && (verstep == LM85_VERSTEP_ADT7463 + || verstep == LM85_VERSTEP_ADT7463C) ) { kind = adt7463 ; } else if( company == LM85_COMPANY_ANALOG_DEV && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC ) { @@ -1107,6 +1122,9 @@ */ kind = emc6d100 ; } else if( company == LM85_COMPANY_SMSC + && verstep == LM85_VERSTEP_EMC6D102) { + kind = emc6d102 ; + } else if( company == LM85_COMPANY_SMSC && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { dev_err(&adapter->dev, "lm85: Detected SMSC chip\n"); dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x" @@ -1142,6 +1160,8 @@ type_name = "adt7463"; } else if ( kind == emc6d100){ type_name = "emc6d100"; + } else if ( kind == emc6d102 ) { + type_name = "emc6d102"; } strlcpy(new_client->name, type_name, I2C_NAME_SIZE); @@ -1259,7 +1279,6 @@ case LM85_REG_FAN_MIN(2) : case LM85_REG_FAN_MIN(3) : case LM85_REG_ALARM1 : /* Read both bytes at once */ - case ADM1027_REG_EXTEND_ADC1 : /* Read two bytes at once */ res = i2c_smbus_read_byte_data(client, reg) & 0xff ; res |= i2c_smbus_read_byte_data(client, reg+1) << 8 ; break ; @@ -1363,10 +1382,25 @@ * more significant bits that are read later. */ if ( (data->type == adm1027) || (data->type == adt7463) ) { - data->extend_adc = - lm85_read_value(client, ADM1027_REG_EXTEND_ADC1); + int ext1 = lm85_read_value(client, + ADM1027_REG_EXTEND_ADC1); + int ext2 = lm85_read_value(client, + ADM1027_REG_EXTEND_ADC2); + int val = (ext1 << 8) + ext2; + + for(i = 0; i <= 4; i++) + data->in_ext[i] = (val>>(i * 2))&0x03; + + for(i = 0; i <= 2; i++) + data->temp_ext[i] = (val>>((i + 5) * 2))&0x03; } + /* adc_scale is 2^(number of LSBs). There are 4 extra bits in + the emc6d102 and 2 in the adt7463 and adm1027. In all + other chips ext is always 0 and the value of scale is + irrelevant. So it is left in 4*/ + data->adc_scale = (data->type == emc6d102 ) ? 16 : 4; + for (i = 0; i <= 4; ++i) { data->in[i] = lm85_read_value(client, LM85_REG_IN(i)); @@ -1403,6 +1437,28 @@ /* More alarm bits */ data->alarms |= lm85_read_value(client, EMC6D100_REG_ALARM3) << 16; + } else if (data->type == emc6d102 ) { + /* Have to read LSB bits after the MSB ones because + the reading of the MSB bits has frozen the + LSBs (backward from the ADM1027). + */ + int ext1 = lm85_read_value(client, + EMC6D102_REG_EXTEND_ADC1); + int ext2 = lm85_read_value(client, + EMC6D102_REG_EXTEND_ADC2); + int ext3 = lm85_read_value(client, + EMC6D102_REG_EXTEND_ADC3); + int ext4 = lm85_read_value(client, + EMC6D102_REG_EXTEND_ADC4); + data->in_ext[0] = ext3 & 0x0f; + data->in_ext[1] = ext4 & 0x0f; + data->in_ext[2] = (ext4 >> 4) & 0x0f; + data->in_ext[3] = (ext3 >> 4) & 0x0f; + data->in_ext[4] = (ext2 >> 4) & 0x0f; + + data->temp_ext[0] = ext1 & 0x0f; + data->temp_ext[1] = ext2 & 0x0f; + data->temp_ext[2] = (ext1 >> 4) & 0x0f; } data->last_reading = jiffies ; diff -Nru a/drivers/i2c/chips/lm90.c b/drivers/i2c/chips/lm90.c --- a/drivers/i2c/chips/lm90.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/lm90.c 2005-03-23 19:38:41 -08:00 @@ -43,6 +43,14 @@ * variants. The extra address and features of the MAX6659 are not * supported by this driver. * + * This driver also supports the ADT7461 chip from Analog Devices but + * only in its "compatability mode". If an ADT7461 chip is found but + * is configured in non-compatible mode (where its temperature + * register values are decoded differently) it is ignored by this + * driver. Complete datasheet can be obtained from Analog's website + * at: + * http://products.analog.com/products/info.asp?product=ADT7461 + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -77,6 +85,7 @@ * LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c. * LM89-1, and LM99-1 have address 0x4d. * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). + * ADT7461 always has address 0x4c. */ static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END }; @@ -86,7 +95,7 @@ * Insmod parameters */ -SENSORS_INSMOD_5(lm90, adm1032, lm99, lm86, max6657); +SENSORS_INSMOD_6(lm90, adm1032, lm99, lm86, max6657, adt7461); /* * The LM90 registers @@ -148,6 +157,19 @@ #define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \ ((val) + 500) / 1000) +/* + * ADT7461 is almost identical to LM90 except that attempts to write + * values that are outside the range 0 < temp < 127 are treated as + * the boundary value. + */ + +#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ + (val) >= 127000 ? 127 : \ + ((val) + 500) / 1000) +#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ + (val) >= 127750 ? 0x7FC0 : \ + ((val) + 125) / 250 * 64) + /* * Functions declaration */ @@ -181,6 +203,7 @@ struct semaphore update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ + int kind; /* registers values */ s8 temp_input1, temp_low1, temp_high1; /* local */ @@ -216,7 +239,10 @@ struct i2c_client *client = to_i2c_client(dev); \ struct lm90_data *data = i2c_get_clientdata(client); \ long val = simple_strtol(buf, NULL, 10); \ - data->value = TEMP1_TO_REG(val); \ + if (data->kind == adt7461) \ + data->value = TEMP1_TO_REG_ADT7461(val); \ + else \ + data->value = TEMP1_TO_REG(val); \ i2c_smbus_write_byte_data(client, reg, data->value); \ return count; \ } @@ -227,7 +253,10 @@ struct i2c_client *client = to_i2c_client(dev); \ struct lm90_data *data = i2c_get_clientdata(client); \ long val = simple_strtol(buf, NULL, 10); \ - data->value = TEMP2_TO_REG(val); \ + if (data->kind == adt7461) \ + data->value = TEMP2_TO_REG_ADT7461(val); \ + else \ + data->value = TEMP2_TO_REG(val); \ i2c_smbus_write_byte_data(client, regh, data->value >> 8); \ i2c_smbus_write_byte_data(client, regl, data->value & 0xff); \ return count; \ @@ -381,6 +410,12 @@ && (reg_config1 & 0x3F) == 0x00 && reg_convrate <= 0x0A) { kind = adm1032; + } else + if (address == 0x4c + && chip_id == 0x51 /* ADT7461 */ + && (reg_config1 & 0x1F) == 0x00 /* check compat mode */ + && reg_convrate <= 0x0A) { + kind = adt7461; } } else if (man_id == 0x4D) { /* Maxim */ @@ -418,11 +453,14 @@ name = "lm86"; } else if (kind == max6657) { name = "max6657"; + } else if (kind == adt7461) { + name = "adt7461"; } /* We can fill in the remaining client fields */ strlcpy(new_client->name, name, I2C_NAME_SIZE); data->valid = 0; + data->kind = kind; init_MUTEX(&data->update_lock); /* Tell the I2C layer a new client has arrived */ diff -Nru a/drivers/i2c/chips/lm92.c b/drivers/i2c/chips/lm92.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/i2c/chips/lm92.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,423 @@ +/* + * lm92 - Hardware monitoring driver + * Copyright (C) 2005 Jean Delvare + * + * Based on the lm90 driver, with some ideas taken from the lm_sensors + * lm92 driver as well. + * + * The LM92 is a sensor chip made by National Semiconductor. It reports + * its own temperature with a 0.0625 deg resolution and a 0.33 deg + * accuracy. Complete datasheet can be obtained from National's website + * at: + * http://www.national.com/pf/LM/LM92.html + * + * This driver also supports the MAX6635 sensor chip made by Maxim. + * This chip is compatible with the LM92, but has a lesser accuracy + * (1.0 deg). Complete datasheet can be obtained from Maxim's website + * at: + * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074 + * + * Since the LM92 was the first chipset supported by this driver, most + * comments will refer to this chipset, but are actually general and + * concern all supported chipsets, unless mentioned otherwise. + * + * Support could easily be added for the National Semiconductor LM76 + * and Maxim MAX6633 and MAX6634 chips, which are mostly compatible + * with the LM92. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + + +/* The LM92 and MAX6635 have 2 two-state pins for address selection, + resulting in 4 possible addresses. */ +static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, + I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm92); + +/* The LM92 registers */ +#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */ +#define LM92_REG_TEMP 0x00 /* 16-bit, RO */ +#define LM92_REG_TEMP_HYST 0x02 /* 16-bit, RW */ +#define LM92_REG_TEMP_CRIT 0x03 /* 16-bit, RW */ +#define LM92_REG_TEMP_LOW 0x04 /* 16-bit, RW */ +#define LM92_REG_TEMP_HIGH 0x05 /* 16-bit, RW */ +#define LM92_REG_MAN_ID 0x07 /* 16-bit, RO, LM92 only */ + +/* The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius, + left-justified in 16-bit registers. No rounding is done, with such + a resolution it's just not worth it. Note that the MAX6635 doesn't + make use of the 4 lower bits for limits (i.e. effective resolution + for limits is 1 degree Celsius). */ +static inline int TEMP_FROM_REG(s16 reg) +{ + return reg / 8 * 625 / 10; +} + +static inline s16 TEMP_TO_REG(int val) +{ + if (val <= -60000) + return -60000 * 10 / 625 * 8; + if (val >= 160000) + return 160000 * 10 / 625 * 8; + return val * 10 / 625 * 8; +} + +/* Alarm flags are stored in the 3 LSB of the temperature register */ +static inline u8 ALARMS_FROM_REG(s16 reg) +{ + return reg & 0x0007; +} + +/* Driver data (common to all clients) */ +static struct i2c_driver lm92_driver; + +/* Client data (each client gets its own) */ +struct lm92_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + s16 temp1_input, temp1_crit, temp1_min, temp1_max, temp1_hyst; +}; + + +/* + * Sysfs attributes and callback functions + */ + +static struct lm92_data *lm92_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm92_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) + || !data->valid) { + dev_dbg(&client->dev, "Updating lm92 data\n"); + data->temp1_input = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP)); + data->temp1_hyst = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP_HYST)); + data->temp1_crit = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP_CRIT)); + data->temp1_min = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP_LOW)); + data->temp1_max = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP_HIGH)); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +#define show_temp(value) \ +static ssize_t show_##value(struct device *dev, char *buf) \ +{ \ + struct lm92_data *data = lm92_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ +} +show_temp(temp1_input); +show_temp(temp1_crit); +show_temp(temp1_min); +show_temp(temp1_max); + +#define set_temp(value, reg) \ +static ssize_t set_##value(struct device *dev, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm92_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + data->value = TEMP_TO_REG(val); \ + i2c_smbus_write_word_data(client, reg, swab16(data->value)); \ + return count; \ +} +set_temp(temp1_crit, LM92_REG_TEMP_CRIT); +set_temp(temp1_min, LM92_REG_TEMP_LOW); +set_temp(temp1_max, LM92_REG_TEMP_HIGH); + +static ssize_t show_temp1_crit_hyst(struct device *dev, char *buf) +{ + struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_crit) + - TEMP_FROM_REG(data->temp1_hyst)); +} +static ssize_t show_temp1_max_hyst(struct device *dev, char *buf) +{ + struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_max) + - TEMP_FROM_REG(data->temp1_hyst)); +} +static ssize_t show_temp1_min_hyst(struct device *dev, char *buf) +{ + struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_min) + + TEMP_FROM_REG(data->temp1_hyst)); +} + +static ssize_t set_temp1_crit_hyst(struct device *dev, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm92_data *data = i2c_get_clientdata(client); + data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - + simple_strtol(buf, NULL, 10); + i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST, + swab16(TEMP_TO_REG(data->temp1_hyst))); + return count; +} + +static ssize_t show_alarms(struct device *dev, char *buf) +{ + struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp1_input)); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL); +static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp1_crit, + set_temp1_crit); +static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp1_crit_hyst, + set_temp1_crit_hyst); +static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp1_min, + set_temp1_min); +static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp1_min_hyst, NULL); +static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_max, + set_temp1_max); +static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp1_max_hyst, NULL); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + + +/* + * Detection and registration + */ + +static void lm92_init_client(struct i2c_client *client) +{ + u8 config; + + /* Start the conversions if needed */ + config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); + if (config & 0x01) + i2c_smbus_write_byte_data(client, LM92_REG_CONFIG, + config & 0xFE); +} + +/* The MAX6635 has no identification register, so we have to use tricks + to identify it reliably. This is somewhat slow. + Note that we do NOT rely on the 2 MSB of the configuration register + always reading 0, as suggested by the datasheet, because it was once + reported not to be true. */ +static int max6635_check(struct i2c_client *client) +{ + u16 temp_low, temp_high, temp_hyst, temp_crit; + u8 conf; + int i; + + /* No manufacturer ID register, so a read from this address will + always return the last read value. */ + temp_low = i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW); + if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_low) + return 0; + temp_high = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HIGH); + if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_high) + return 0; + + /* Limits are stored as integer values (signed, 9-bit). */ + if ((temp_low & 0x7f00) || (temp_high & 0x7f00)) + return 0; + temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HYST); + temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TEMP_CRIT); + if ((temp_hyst & 0x7f00) || (temp_crit & 0x7f00)) + return 0; + + /* Registers addresses were found to cycle over 16-byte boundaries. + We don't test all registers with all offsets so as to save some + reads and time, but this should still be sufficient to dismiss + non-MAX6635 chips. */ + conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); + for (i=16; i<96; i*=2) { + if (temp_hyst != i2c_smbus_read_word_data(client, + LM92_REG_TEMP_HYST + i - 16) + || temp_crit != i2c_smbus_read_word_data(client, + LM92_REG_TEMP_CRIT + i) + || temp_low != i2c_smbus_read_word_data(client, + LM92_REG_TEMP_LOW + i + 16) + || temp_high != i2c_smbus_read_word_data(client, + LM92_REG_TEMP_HIGH + i + 32) + || conf != i2c_smbus_read_byte_data(client, + LM92_REG_CONFIG + i)) + return 0; + } + + return 1; +} + +/* The following function does more than just detection. If detection + succeeds, it also registers the new chip. */ +static int lm92_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct lm92_data *data; + int err = 0; + char *name; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_WORD_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct lm92_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm92_data)); + + /* Fill in enough client fields so that we can read from the chip, + which is required for identication */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm92_driver; + new_client->flags = 0; + + /* A negative kind means that the driver was loaded with no force + parameter (default), so we must identify the chip. */ + if (kind < 0) { + u8 config = i2c_smbus_read_byte_data(new_client, + LM92_REG_CONFIG); + u16 man_id = i2c_smbus_read_word_data(new_client, + LM92_REG_MAN_ID); + + if ((config & 0xe0) == 0x00 + && man_id == 0x0180) { + pr_info("lm92: Found National Semiconductor LM92 chip\n"); + kind = lm92; + } else + if (max6635_check(new_client)) { + pr_info("lm92: Found Maxim MAX6635 chip\n"); + kind = lm92; /* No separate prefix */ + } + else + goto exit_free; + } else + if (kind == 0) /* Default to an LM92 if forced */ + kind = lm92; + + /* Give it the proper name */ + if (kind == lm92) { + name = "lm92"; + } else { /* Supposedly cannot happen */ + dev_dbg(&new_client->dev, "Kind out of range?\n"); + goto exit_free; + } + + /* Fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the i2c subsystem a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the chipset */ + lm92_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_crit); + device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int lm92_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm92_detect); +} + +static int lm92_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + + +/* + * Module and driver stuff + */ + +static struct i2c_driver lm92_driver = { + .owner = THIS_MODULE, + .name = "lm92", + .id = I2C_DRIVERID_LM92, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm92_attach_adapter, + .detach_client = lm92_detach_client, +}; + +static int __init sensors_lm92_init(void) +{ + return i2c_add_driver(&lm92_driver); +} + +static void __exit sensors_lm92_exit(void) +{ + i2c_del_driver(&lm92_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("LM92/MAX6635 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm92_init); +module_exit(sensors_lm92_exit); diff -Nru a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c --- a/drivers/i2c/chips/m41t00.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/m41t00.c 2005-03-23 19:38:41 -08:00 @@ -184,7 +184,6 @@ memset(client, 0, sizeof(struct i2c_client)); strncpy(client->name, M41T00_DRV_NAME, I2C_NAME_SIZE); - client->id = m41t00_driver.id; client->flags = I2C_DF_NOTIFY; client->addr = addr; client->adapter = adap; diff -Nru a/drivers/i2c/chips/w83627hf.c b/drivers/i2c/chips/w83627hf.c --- a/drivers/i2c/chips/w83627hf.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/chips/w83627hf.c 2005-03-23 19:38:41 -08:00 @@ -304,7 +304,6 @@ u32 beep_mask; /* Register encoding, combined */ u8 beep_enable; /* Boolean */ u8 pwm[3]; /* Register value */ - u8 pwmenable[3]; /* bool */ u16 sens[3]; /* 782D/783S only. 1 = pentium diode; 2 = 3904 diode; 3000-5000 = thermistor beta. @@ -1316,10 +1315,6 @@ if ((type == w83697hf) && (i == 2)) break; } - - data->pwmenable[0] = 1; - data->pwmenable[1] = 1; - data->pwmenable[2] = 1; if(init) { /* Enable temp2 */ diff -Nru a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c --- a/drivers/i2c/i2c-core.c 2005-03-23 19:38:41 -08:00 +++ b/drivers/i2c/i2c-core.c 2005-03-23 19:38:41 -08:00 @@ -587,7 +587,13 @@ int ret; if (adap->algo->master_xfer) { - dev_dbg(&adap->dev, "master_xfer: with %d msgs.\n", num); +#ifdef DEBUG + for (ret = 0; ret < num; ret++) { + dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, " + "len=%d\n", ret, msgs[ret].flags & I2C_M_RD ? + 'R' : 'W', msgs[ret].addr, msgs[ret].len); + } +#endif down(&adap->bus_lock); ret = adap->algo->master_xfer(adap,msgs,num); diff -Nru a/drivers/superio/Kconfig b/drivers/superio/Kconfig --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/Kconfig 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,56 @@ +menu "SuperIO subsystem support" + +config SC_SUPERIO + tristate "SuperIO subsystem support" + depends on CONNECTOR + help + SuperIO subsystem support. + + This support is also available as a module. If so, the module + will be called superio.ko. + +config SC_PC8736X + tristate "PC8736x SuperIO" + depends on SC_SUPERIO + help + Say Y here if you want to use PC8736x controller. + It is LPC SuperIO with hardware monitoring chip from National Semiconductor. + + This support is also available as a module. If so, the module + will be called pc8736x.ko. + +config SC_SCX200 + tristate "SCx200/SC1100 SuperIO" + depends on SC_SUPERIO + help + Say Y here if you want to use SCx200/SC1100 controller. + It is Geode system-on-chip processor from AMD(formerly National Semiconductor). + + This support is also available as a module. If so, the module + will be called scx200.ko. + + +config SC_GPIO + tristate "SuperIO - GPIO" + depends on SC_SUPERIO + help + Say Y here if you want to use General-Purpose Input/Output (GPIO) pins. + + This support is also available as a module. If so, the module + will be called sc_gpio.ko. + +config SC_ACB + tristate "SuperIO - Access Bus" + depends on SC_SUPERIO + help + Say Y here if you want to use Access Bus. + The ACB is a two-wire synchronous serial interface compatible with the ACCESS.bus physical layer. + The ACB is also compatible with Intel's SMBus and Philips' I2C. + The ACB allows easy interfacing to a wide range of low-cost memories and I/O devices, + including EEPROMs, SRAMs, timers, ADC, DAC, clock chips and peripheral drivers. + + This support is also available as a module. If so, the module + will be called sc_acb.ko. + + +endmenu diff -Nru a/drivers/superio/Makefile b/drivers/superio/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/Makefile 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,11 @@ +# +# Makefile for the SuperIO subsystem. +# + +obj-$(CONFIG_SC_SUPERIO) += superio.o +obj-$(CONFIG_SC_GPIO) += sc_gpio.o +obj-$(CONFIG_SC_ACB) += sc_acb.o +obj-$(CONFIG_SC_PC8736X) += pc8736x.o +obj-$(CONFIG_SC_SCX200) += scx.o + +superio-objs := sc.o chain.o sc_conn.o diff -Nru a/drivers/superio/chain.c b/drivers/superio/chain.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/chain.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,52 @@ +/* + * chain.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include +#include + +#include "chain.h" + +struct dev_chain *chain_alloc(void *ptr) +{ + struct dev_chain *ch; + + ch = kmalloc(sizeof(struct dev_chain), GFP_ATOMIC); + if (!ch) { + printk(KERN_ERR "Failed to allocate new chain for %p.\n", ptr); + return NULL; + } + + memset(ch, 0, sizeof(struct dev_chain)); + + ch->ptr = ptr; + + return ch; +} + +void chain_free(struct dev_chain *ch) +{ + memset(ch, 0, sizeof(struct dev_chain)); + kfree(ch); +} diff -Nru a/drivers/superio/chain.h b/drivers/superio/chain.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/chain.h 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,37 @@ +/* + * chain.h + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __CHAIN_H +#define __CHAIN_H + +#include + +struct dev_chain +{ + struct list_head chain_entry; + void *ptr; +}; + +struct dev_chain *chain_alloc(void *ptr); +void chain_free(struct dev_chain *ch); + +#endif /* __CHAIN_H */ diff -Nru a/drivers/superio/pc8736x.c b/drivers/superio/pc8736x.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/pc8736x.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,209 @@ +/* + * pc8736x.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sc.h" +#include "pc8736x.h" +#include "sc_gpio.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Driver for PC87366 SuperIO chip."); + +static int pc8736x_probe(void *, unsigned long base); +static int pc8736x_activate_one_logical(struct logical_dev *ldev); +static int pc8736x_deactivate_one_logical(struct logical_dev *ldev); + +static struct sc_dev pc8736x_dev = { + .name = "PC8736X", + .probe = pc8736x_probe, + .activate_one = pc8736x_activate_one_logical, + .deactivate_one = pc8736x_deactivate_one_logical, + //.read = pc8736x_read, + //.write = pc8736x_write, +}; + +static struct sc_chip_id pc8736x_sio_ids[] = { + {"PC87360", 0xe1}, + {"PC87363", 0xe8}, + {"PC87364", 0xe4}, + {"PC87365", 0xe5}, + {"PC87366", 0xe9}, +}; + +void pc8736x_write_reg(struct sc_dev *dev, u8 reg, u8 val) +{ + outb(reg, dev->base_index); + outb(val, dev->base_data); +} + +u8 pc8736x_read_reg(struct sc_dev *dev, u8 reg) +{ + u8 val; + + outb(reg, dev->base_index); + val = inb(dev->base_data); + + return val; +} + +static int pc8736x_chip_index(u8 id) +{ + int i; + + for (i = 0; i < sizeof(pc8736x_sio_ids) / sizeof(pc8736x_sio_ids[0]); ++i) + if (pc8736x_sio_ids[i].id == id) + return i; + + return -ENODEV; +} + +static int pc8736x_probe(void *data, unsigned long base) +{ + unsigned long size = 2; + u8 id; + int chip_num; + struct sc_dev *dev = (struct sc_dev *)data; + + /* + * Special address to handle. + */ + if (base == 0) + return -ENODEV; + + dev->base_index = base; + dev->base_data = base + 1; + + id = pc8736x_read_reg(dev, SIO_REG_SID); + chip_num = pc8736x_chip_index(id); + + if (chip_num >= 0) { + printk(KERN_INFO "Found %s [0x%x] at 0x%04lx-0x%04lx.\n", + pc8736x_sio_ids[chip_num].name, + pc8736x_sio_ids[chip_num].id, + base, base + size - 1); + return 0; + } + + printk(KERN_INFO "Found nothing at 0x%04lx-0x%04lx.\n", + base, base + size - 1); + + return -ENODEV; +} + +static int pc8736x_deactivate_one_logical(struct logical_dev *ldev) +{ + return 0; +} + +static int pc8736x_activate_one_logical(struct logical_dev *ldev) +{ + int err; + struct sc_dev *dev = ldev->pdev; + u8 active; + + pc8736x_write_reg(dev, SIO_REG_LDN, ldev->index); + active = pc8736x_read_reg(dev, SIO_REG_ACTIVE); + if ((active & SIO_ACTIVE_EN) == 0) { + printk(KERN_INFO "\t%16s - not activated at %x: activating... ", + ldev->name, ldev->index); + + pc8736x_write_reg(dev, SIO_REG_ACTIVE, active | SIO_ACTIVE_EN); + active = pc8736x_read_reg(dev, SIO_REG_ACTIVE); + if ((active & SIO_ACTIVE_EN) == 0) { + printk("failed.\n"); + return -ENODEV; + } + printk("done\n"); + } + + pc8736x_write_reg(dev, SIO_REG_IRQ, ldev->irq); + ldev->irq = pc8736x_read_reg(dev, SIO_REG_IRQ); + + ldev->irq_type = pc8736x_read_reg(dev, SIO_REG_IRQ_TYPE); + ldev->base_addr = pc8736x_read_reg(dev, SIO_REG_IO_LSB); + ldev->base_addr |= (pc8736x_read_reg(dev, SIO_REG_IO_MSB) << 8); + + err = ldev->activate(ldev); + if (err < 0) { + printk(KERN_INFO "\t%16s - not activated: ->activate() failed with error code %d.\n", + ldev->name, err); + return -ENODEV; + } + + printk(KERN_INFO "\t%16s - activated: 0x%04lx-0x%04lx, irq=%02x [type=%02x]\n", + ldev->name, ldev->base_addr, ldev->base_addr + ldev->range, + ldev->irq, ldev->irq_type); + + return 0; +} + +static int pc8736x_init(void) +{ + int err; + + err = sc_add_sc_dev(&pc8736x_dev); + if (err) + return err; + + printk(KERN_INFO "Driver for %s SuperIO chip.\n", pc8736x_dev.name); + return 0; +} + +static void pc8736x_fini(void) +{ + sc_del_sc_dev(&pc8736x_dev); + + while (atomic_read(&pc8736x_dev.refcnt)) { + printk(KERN_INFO "Waiting for %s to became free: refcnt=%d.\n", + pc8736x_dev.name, atomic_read(&pc8736x_dev.refcnt)); + + msleep_interruptible(1000); + + if (signal_pending(current)) + flush_signals(current); + + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + + } +} + +module_init(pc8736x_init); +module_exit(pc8736x_fini); + +EXPORT_SYMBOL(pc8736x_write_reg); +EXPORT_SYMBOL(pc8736x_read_reg); diff -Nru a/drivers/superio/pc8736x.h b/drivers/superio/pc8736x.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/pc8736x.h 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,39 @@ +/* + * pc8736x.h + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PC8736X_H +#define __PC8736X_H + +#define SIO_GPDO0 0x00 +#define SIO_GPDI0 0x01 +#define SIO_GPEVEN0 0x02 +#define SIO_GPEVST0 0x03 +#define SIO_GPDO1 0x04 +#define SIO_GPDI1 0x05 +#define SIO_GPEVEN1 0x06 +#define SIO_GPEVST1 0x07 +#define SIO_GPDO2 0x08 +#define SIO_GPDI2 0x09 +#define SIO_GPDO3 0x0A +#define SIO_GPDI3 0x0B + +#endif /* __PC8736X_H */ diff -Nru a/drivers/superio/pin_test.c b/drivers/superio/pin_test.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/pin_test.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,93 @@ +/* + * pin_test.c + * + * Copyright (c) 2004 Evgeniy Polyakov + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sc.h" +#include "sc_gpio.h" + +MODULE_LICENSE ("GPL"); + +static int test_pin = 21; +module_param(test_pin, int, 0); + +static struct timer_list tm; +static struct logical_dev *ldev; + +static void tm_func(unsigned long data) +{ + int i; + int val; + + for (i=0; iread(ldev, i); + printk("%02d.%d ", i, val); + if (i % 8 == 7) + printk("\n"); + + if (i == test_pin) + ldev->write(ldev, i, (val)?0:1); + } + printk("\n"); + + mod_timer(&tm, jiffies + HZ); +} + +int __devinit tm_init (void) +{ + int i; + + ldev = sc_get_ldev("GPIO"); + if (!ldev) + { + printk(KERN_ERR "Logical device GPIO is not registered.\n"); + return -ENODEV; + } + for (i=0; icontrol(ldev, i, ~0, SIO_GPIO_CONF_PUSHPULL); + + init_timer(&tm); + tm.expires = jiffies + HZ; + tm.function = tm_func; + tm.data = 0; + add_timer(&tm); + + return 0; +} + +void __devexit tm_fini(void) +{ + del_timer_sync(&tm); + sc_put_ldev(ldev); +} + +module_init(tm_init); +module_exit(tm_fini); diff -Nru a/drivers/superio/sc.c b/drivers/superio/sc.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/sc.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,802 @@ +/* + * sc.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sc.h" +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Generic SuperIO driver."); + +static unsigned long base_addr[] = { 0x2e, 0x4e }; + +static spinlock_t sdev_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(sdev_list); + +static spinlock_t ldev_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(ldev_list); + +static int sc_activate_logical(struct sc_dev *, struct logical_dev *); +static void sc_deactivate_logical(struct sc_dev *, struct logical_dev *); + +static int __devinit sc_init(void); +static void __devexit sc_fini(void); + +static inline int sc_ldev_equal(struct logical_dev *l1, struct logical_dev *l2) +{ + int a, b; + + a = b = 1; + + a = (!strncmp(l1->name, l2->name, SC_NAME_LEN) && l1->index == l2->index); + + if (sc_ldev_is_clone(l1) && sc_ldev_is_clone(l2)) + b = (l1->base_addr == l2->base_addr); + + return (a && b); +} + +static inline int sc_ldev_equal_name(struct logical_dev *l1, + struct logical_dev *l2) +{ + return (!strncmp(l1->name, l2->name, SC_NAME_LEN)); +} + +static inline int sc_sdev_equal(struct sc_dev *s1, struct sc_dev *s2) +{ + return (!strncmp(s1->name, s2->name, SC_NAME_LEN)); +} + +static void sc_del_sdev_from_ldev(struct sc_dev *sdev, struct logical_dev *ldev) +{ + struct sc_dev *__sdev; + struct dev_chain *ch, *n; + + spin_lock(&ldev->chain_lock); + + list_for_each_entry_safe(ch, n, &ldev->chain_list, chain_entry) { + __sdev = ch->ptr; + + if (sc_sdev_equal(__sdev, sdev)) { + list_del(&ch->chain_entry); + chain_free(ch); + smp_mb__before_atomic_dec(); + atomic_dec(&__sdev->refcnt); + smp_mb__after_atomic_dec(); + break; + } + } + + spin_unlock(&ldev->chain_lock); +} + +static int __sc_add_logical_dev(struct sc_dev *dev, struct logical_dev *__ldev) +{ + int err; + struct logical_dev *ldev; + struct dev_chain *ch, *lch, *_ch; + int __found = 0; + + err = 0; + + list_for_each_entry(_ch, &dev->chain_list, chain_entry) { + ldev = _ch->ptr; + + if (sc_ldev_equal(ldev, __ldev)) { + printk(KERN_INFO "Logical device %s already registered in SuperIO chip %s.\n", + ldev->name, dev->name); + err++; + break; + } + } + + if (err) { + err = -ENODEV; + goto err_out; + } + + if (!sc_ldev_is_clone(__ldev)) { + struct sc_chip_id *cid; + + /* + * SuperIO core is registering logical device. + * SuperIO chip knows where it must live. + * If logical device being registered lives at the different location + * (for example when it was registered for all devices, + * but has address(index) corresponding to only one SuperIO chip) + * then we will register new logical device with the same name + * but with the different location(index). + * + * It is called clone. + */ + + for (cid = dev->ldevs; cid && strlen(cid->name); ++cid) { + if (!strncmp(cid->name, __ldev->name, SC_NAME_LEN) + && cid->id != __ldev->index) { + struct logical_dev *clone; + + __found = 1; + + printk(KERN_INFO "Logical device %s in chip %s lives at %x, but provided address %x.\n" + "Registering new logical device %s in chip %s with address %x.\n", + __ldev->name, dev->name, cid->id, + __ldev->index, __ldev->name, dev->name, + cid->id); + + clone = sc_ldev_clone(__ldev); + if (!clone) { + err = -ENOMEM; + continue; + } + + /* + * If logical device provided 0xFF index, than it is mean that + * SuperIO chip driver must handle this situation. + * It is similar to the zero base address in SuperIO ->probe() function. + */ + + clone->index = cid->id; + + err = __sc_add_logical_dev(dev, clone); + if (err) + sc_ldev_unclone(clone); + } + } + + if (__found) + return 0; + } + + __ldev->pdev = dev; + err = sc_activate_logical(dev, __ldev); + if (err) { + printk(KERN_INFO "Logical device %s is not found in SuperIO chip %s.\n", + __ldev->name, dev->name); + err = -EINVAL; + goto err_out; + } + + ch = chain_alloc(dev); + if (!ch) { + err = -ENOMEM; + goto err_out; + } + + lch = chain_alloc(__ldev); + if (!lch) { + err = -ENOMEM; + goto err_out_free_chain; + } + + ch->ptr = dev; + + spin_lock(&__ldev->chain_lock); + smp_mb__before_atomic_inc(); + atomic_inc(&__ldev->refcnt); + smp_mb__after_atomic_inc(); + list_add_tail(&ch->chain_entry, &__ldev->chain_list); + spin_unlock(&__ldev->chain_lock); + + smp_mb__before_atomic_inc(); + atomic_inc(&dev->refcnt); + smp_mb__after_atomic_inc(); + list_add_tail(&lch->chain_entry, &dev->chain_list); + + __found = 0; + spin_lock(&ldev_lock); + list_for_each_entry(ldev, &ldev_list, ldev_entry) { + if (sc_ldev_equal(ldev, __ldev)) { + __found = 1; + break; + } + } + + if (!__found) { + list_add(&__ldev->ldev_entry, &ldev_list); + } + + spin_unlock(&ldev_lock); + + return 0; + + chain_free(lch); +err_out_free_chain: + chain_free(ch); +err_out: + + return err; +} + +int sc_add_logical_dev(struct sc_dev *sdev, struct logical_dev *__ldev) +{ + struct sc_dev *dev; + int err, found = 0; + + printk(KERN_INFO "Adding logical device %s [%x] [%s].\n", + __ldev->name, __ldev->index, + (sc_ldev_is_clone(__ldev)) ? "clone" : "not clone"); + + spin_lock_init(&__ldev->chain_lock); + INIT_LIST_HEAD(&__ldev->chain_list); + + spin_lock_init(&__ldev->lock); + + atomic_set(&__ldev->refcnt, 0); + + if (sdev) { + spin_lock(&sdev->lock); + spin_lock(&sdev->chain_lock); + err = __sc_add_logical_dev(sdev, __ldev); + spin_unlock(&sdev->chain_lock); + spin_unlock(&sdev->lock); + + if (!err) + found = 1; + + goto finish; + } + + spin_lock(&sdev_lock); + list_for_each_entry(dev, &sdev_list, sdev_entry) { + spin_lock(&dev->lock); + spin_lock(&dev->chain_lock); + err = __sc_add_logical_dev(dev, __ldev); + spin_unlock(&dev->chain_lock); + spin_unlock(&dev->lock); + if (!err) + found = 1; + } + spin_unlock(&sdev_lock); + +finish: + + return (found) ? 0 : -ENODEV; +} + +/* + * Must be called under ldev->chain_lock and ldev_lock held. + */ +static void __sc_del_logical_dev(struct sc_dev *dev, struct logical_dev *__ldev) +{ + struct dev_chain *ch, *n; + struct logical_dev *ldev; + + spin_lock(&dev->chain_lock); + list_for_each_entry_safe(ch, n, &dev->chain_list, chain_entry) { + ldev = ch->ptr; + + if (sc_ldev_equal(ldev, __ldev)) { + list_del(&ch->chain_entry); + smp_mb__before_atomic_dec(); + atomic_dec(&ldev->refcnt); + smp_mb__after_atomic_dec(); + chain_free(ch); + + sc_deactivate_logical(dev, ldev); + + break; + } + } + spin_unlock(&dev->chain_lock); +} + +void sc_del_logical_dev(struct logical_dev *ldev) +{ + struct sc_dev *dev; + struct dev_chain *ch, *n; + struct logical_dev *ld, *ln; + + spin_lock(&ldev->lock); + + spin_lock(&ldev->chain_lock); + list_for_each_entry_safe(ch, n, &ldev->chain_list, chain_entry) { + dev = ch->ptr; + + spin_lock(&dev->lock); + printk(KERN_INFO "Deactivating %s [%s/%s] from %s\n", + ldev->name, + (sc_ldev_is_clone(ldev)) ? "clone" : "not clone", + (sc_ldev_cloned(ldev)) ? "cloned" : "not cloned", + dev->name); + __sc_del_logical_dev(dev, ldev); + + list_del(&ch->chain_entry); + smp_mb__before_atomic_dec(); + atomic_dec(&dev->refcnt); + smp_mb__after_atomic_dec(); + chain_free(ch); + spin_unlock(&dev->lock); + } + spin_unlock(&ldev->chain_lock); + + if (sc_ldev_is_clone(ldev)) { + spin_unlock(&ldev->lock); + return; + } + + spin_lock(&ldev_lock); + list_for_each_entry_safe(ld, ln, &ldev_list, ldev_entry) { + printk(KERN_INFO "Processing ldev %s [%s/%s] [%x]\n", + ld->name, + (sc_ldev_is_clone(ld)) ? "clone" : "not clone", + (sc_ldev_cloned(ld)) ? "cloned" : "not cloned", + ld->index); + if (sc_ldev_equal(ld, ldev)) { + list_del(&ld->ldev_entry); + } else if (sc_ldev_cloned(ldev)) { + /* + * When logical device is clonned + * clone's chunks can point to the diferent device + * than origianl logical device's chunks. + * Since we do not have backlink from the original device + * to it's clones we must run through the whole ldev_list. + */ + + if (sc_ldev_is_clone(ld) && sc_ldev_equal_name(ld, ldev)) { + list_del(&ld->ldev_entry); + sc_del_logical_dev(ld); + sc_ldev_unclone(ld); + } + } + } + spin_unlock(&ldev_lock); + + spin_unlock(&ldev->lock); + + while (atomic_read(&ldev->refcnt)) { + printk(KERN_INFO "Waiting logical device %s [%x] [%s] to become free: refcnt=%d.\n", + ldev->name, ldev->index, + (sc_ldev_is_clone(ldev)) ? "clone" : "not clone", + atomic_read(&ldev->refcnt)); + + msleep_interruptible(1000); + + if (signal_pending(current)) + flush_signals(current); + + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + } + +} + +static int sc_check_sc_dev(struct sc_dev *dev) +{ + if (!dev->activate_one) { + printk(KERN_ERR "SuperIO device %s does not have ->activate_one() method.\n", + dev->name); + return -EINVAL; + } + + if (!dev->probe) { + printk(KERN_ERR "SuperIO device %s does not have ->probe() method.\n", + dev->name); + return -EINVAL; + } + + if (!dev->ldevs) + printk(KERN_INFO "SuperIO device %s does not have logical device table.\n", + dev->name); + + return 0; +} + +int sc_add_sc_dev(struct sc_dev *__sdev) +{ + int i, err; + struct sc_dev *sdev; + + if (sc_check_sc_dev(__sdev)) + return -EINVAL; + + spin_lock_init(&__sdev->chain_lock); + INIT_LIST_HEAD(&__sdev->chain_list); + + spin_lock_init(&__sdev->lock); + + spin_lock(&sdev_lock); + list_for_each_entry(sdev, &sdev_list, sdev_entry) { + if (sc_sdev_equal(sdev, __sdev)) { + printk(KERN_INFO "Super IO chip %s already registered.\n", + sdev->name); + spin_unlock(&sdev_lock); + return -EINVAL; + } + } + + err = -ENODEV; + for (i = 0; i < sizeof(base_addr) / sizeof(base_addr[0]); ++i) { + err = __sdev->probe(__sdev, base_addr[i]); + if (!err) + break; + } + + /* + * Special case for non standard base location. + */ + if (i == sizeof(base_addr) / sizeof(base_addr[0])) + err = __sdev->probe(__sdev, 0); + + if (!err) { + atomic_set(&__sdev->refcnt, 0); + list_add_tail(&__sdev->sdev_entry, &sdev_list); + } + + spin_unlock(&sdev_lock); + + return err; +} + +void sc_del_sc_dev(struct sc_dev *__sdev) +{ + struct dev_chain *ch, *n; + struct logical_dev *ldev; + struct sc_dev *sdev, *sn; + + spin_lock(&__sdev->lock); + spin_lock(&sdev_lock); + list_for_each_entry_safe(sdev, sn, &sdev_list, sdev_entry) { + if (sc_sdev_equal(sdev, __sdev)) { + list_del(&sdev->sdev_entry); + break; + } + } + spin_unlock(&sdev_lock); + + spin_lock(&__sdev->chain_lock); + list_for_each_entry_safe(ch, n, &__sdev->chain_list, chain_entry) { + ldev = ch->ptr; + + list_del(&ch->chain_entry); + smp_mb__before_atomic_dec(); + atomic_dec(&ldev->refcnt); + smp_mb__after_atomic_dec(); + chain_free(ch); + + sc_deactivate_logical(__sdev, ldev); + sc_del_sdev_from_ldev(__sdev, ldev); + } + spin_unlock(&__sdev->chain_lock); + spin_unlock(&__sdev->lock); + + while (atomic_read(&__sdev->refcnt)) { + printk(KERN_INFO "Waiting SuperIO chip %s to become free: refcnt=%d.\n", + __sdev->name, atomic_read(&__sdev->refcnt)); + + msleep_interruptible(1000); + + if (signal_pending(current)) + flush_signals(current); + + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + } +} + +static void sc_deactivate_logical(struct sc_dev *dev, struct logical_dev *ldev) +{ + printk(KERN_INFO "Deactivating logical device %s in SuperIO chip %s... ", + ldev->name, dev->name); + + if (ldev->irq) + { + free_irq(ldev->irq, ldev); + ldev->irq = 0; + } + + + if (dev->deactivate_one) + dev->deactivate_one(ldev); + + printk("done.\n"); +} + +/* + * Must be called under sdev_lock held. + */ +static int sc_activate_logical(struct sc_dev *dev, struct logical_dev *ldev) +{ + int err; + + printk(KERN_INFO "Activating logical device %s [%x].\n", ldev->name, + ldev->index); + + if (ldev->irq && !ldev->irq_handler) + ldev->irq = 0; + + ldev->pdev = dev; + err = dev->activate_one(ldev); + if (err) + return err; + + if (ldev->irq) + { + err = request_irq(ldev->irq, ldev->irq_handler, SA_SHIRQ | SA_INTERRUPT, ldev->name, ldev); + if (err) + { + printk(KERN_ERR "Failed to request irq %d: err=%d. Disabling interrupt.\n", + ldev->irq, err); + ldev->irq = 0; + } + } + + + + return err; +} + +struct sc_dev *sc_get_sdev(char *name) +{ + struct sc_dev *sdev; + + spin_lock(&sdev_lock); + list_for_each_entry(sdev, &sdev_list, sdev_entry) { + if (!strcmp(name, sdev->name)) { + atomic_inc(&sdev->refcnt); + smp_mb__after_atomic_inc(); + spin_unlock(&sdev_lock); + return sdev; + } + } + spin_unlock(&sdev_lock); + + return NULL; +} + +void sc_put_sdev(struct sc_dev *sdev) +{ + smp_mb__before_atomic_dec(); + atomic_dec(&sdev->refcnt); + smp_mb__after_atomic_dec(); +} + +/* + * Get logical device which has given name and index. + */ +struct logical_dev *sc_get_ldev_index(char *name, u8 index) +{ + struct logical_dev *ldev; + + spin_lock(&ldev_lock); + list_for_each_entry(ldev, &ldev_list, ldev_entry) { + if (!strcmp(name, ldev->name) && ldev->index == index) { + atomic_inc(&ldev->refcnt); + smp_mb__after_atomic_inc(); + spin_unlock(&ldev_lock); + return ldev; + } + } + spin_unlock(&ldev_lock); + + return NULL; +} + +/* + * Get the first logical device with the given name. + */ +struct logical_dev *sc_get_ldev(char *name) +{ + struct logical_dev *ldev; + + spin_lock(&ldev_lock); + list_for_each_entry(ldev, &ldev_list, ldev_entry) { + if (!strcmp(name, ldev->name)) { + atomic_inc(&ldev->refcnt); + smp_mb__after_atomic_inc(); + spin_unlock(&ldev_lock); + return ldev; + } + } + spin_unlock(&ldev_lock); + + return NULL; +} + +/* + * Get the first logical device with the given name connected to given SuperIO chip. + */ +struct logical_dev *sc_get_ldev_in_sdev(char *name, struct sc_dev *sdev) +{ + struct dev_chain *ch; + struct logical_dev *ldev; + + spin_lock(&sdev->chain_lock); + list_for_each_entry(ch, &sdev->chain_list, chain_entry) { + ldev = ch->ptr; + + if (!strcmp(name, ldev->name)) { + atomic_inc(&ldev->refcnt); + smp_mb__after_atomic_inc(); + spin_unlock(&sdev->chain_lock); + return ldev; + } + } + spin_unlock(&sdev->chain_lock); + + return NULL; +} + +void sc_put_ldev(struct logical_dev *ldev) +{ + smp_mb__before_atomic_dec(); + atomic_dec(&ldev->refcnt); + smp_mb__after_atomic_dec(); +} + +/* + * Cloned logical device has the same structure as original device. + * Although cloned and original devices do not cross, they both point + * to the same block of the memory(they have pointers to the same functions), + * so we will increment reference counter for original device + * like cloned device has a reference to it. + */ +struct logical_dev *sc_ldev_clone(struct logical_dev *ldev) +{ + struct logical_dev *__ldev; + + __ldev = sc_ldev_alloc(ldev->name, ldev->index); + if (!__ldev) + return NULL; + + memcpy(__ldev, ldev, sizeof(*__ldev)); + + spin_lock_init(&__ldev->chain_lock); + INIT_LIST_HEAD(&__ldev->chain_list); + spin_lock_init(&__ldev->lock); + + atomic_inc(&ldev->refcnt); + smp_mb__after_atomic_inc(); + set_bit(LDEV_CLONED, (long *)&ldev->flags); + + atomic_set(&__ldev->refcnt, 0); + __ldev->orig_ldev = ldev; + + return __ldev; +} + +int sc_ldev_is_clone(struct logical_dev *ldev) +{ + return (ldev->orig_ldev) ? 1 : 0; +} + +int sc_ldev_cloned(struct logical_dev *ldev) +{ + return (test_bit(LDEV_CLONED, (long *)&ldev->flags) + && (atomic_read(&ldev->refcnt) >= 1)); +} + +void sc_ldev_unclone(struct logical_dev *clone) +{ + struct logical_dev *orig = clone->orig_ldev; + + if (!sc_ldev_is_clone(clone)) { + printk(KERN_INFO "Logical device %s is not clone.\n", + clone->name); + return; + } + + if (atomic_dec_and_test(&orig->refcnt)) + clear_bit(LDEV_CLONED, (long *)&orig->flags); + + memset(clone, 0, sizeof(*clone)); + kfree(clone); + clone = NULL; +} + +struct logical_dev *sc_ldev_alloc(char *name, u8 index) +{ + struct logical_dev *ldev; + + ldev = kmalloc(sizeof(*ldev), GFP_ATOMIC); + if (!ldev) { + printk(KERN_ERR "Failed to allocate new logical device %s at address %x.\n", + name, index); + return NULL; + } + + memset(ldev, 0, sizeof(*ldev)); + + snprintf(ldev->name, sizeof(ldev->name), "%s", name); + ldev->index = index; + + return ldev; +} + +void sc_ldev_free(struct logical_dev *ldev) +{ + if (ldev->orig_ldev) { + struct logical_dev *orig = ldev->orig_ldev; + /* + * It is clone. + */ + if (!atomic_dec_and_test(&ldev->refcnt)) { + /* + * It is impossible, clone can not have clones. + */ + printk(KERN_INFO "Logical device clone %s has refcnt=%d and flags=%x.\n", + ldev->name, atomic_read(&ldev->refcnt), + ldev->flags); + BUG(); + } + + spin_lock(&orig->lock); + + clear_bit(LDEV_CLONED, (long *)&orig->flags); + smp_mb__before_atomic_dec(); + atomic_dec(&orig->refcnt); + smp_mb__after_atomic_dec(); + + memset(ldev, 0, sizeof(*ldev)); + kfree(ldev); + + spin_unlock(&orig->lock); + } else if (sc_ldev_cloned(ldev)) { + /* + * It is cloned. + */ + } +} + +static int __devinit sc_init(void) +{ + printk(KERN_INFO "SuperIO driver is starting...\n"); + + return sc_register_callback(); +} + +static void __devexit sc_fini(void) +{ + sc_unregister_callback(); + printk(KERN_INFO "SuperIO driver finished.\n"); +} + +module_init(sc_init); +module_exit(sc_fini); + +EXPORT_SYMBOL(sc_add_logical_dev); +EXPORT_SYMBOL(sc_del_logical_dev); +EXPORT_SYMBOL(sc_get_ldev); +EXPORT_SYMBOL(sc_get_ldev_in_sdev); +EXPORT_SYMBOL(sc_put_ldev); +EXPORT_SYMBOL(sc_add_sc_dev); +EXPORT_SYMBOL(sc_del_sc_dev); +EXPORT_SYMBOL(sc_get_sdev); +EXPORT_SYMBOL(sc_put_sdev); +EXPORT_SYMBOL(sc_ldev_alloc); +EXPORT_SYMBOL(sc_ldev_free); +EXPORT_SYMBOL(sc_ldev_clone); +EXPORT_SYMBOL(sc_ldev_unclone); +EXPORT_SYMBOL(sc_ldev_cloned); +EXPORT_SYMBOL(sc_ldev_is_clone); diff -Nru a/drivers/superio/sc.h b/drivers/superio/sc.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/sc.h 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,134 @@ +/* + * sc.h + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SC_H +#define __SC_H + +#include +#include + +#include "chain.h" + +#define SC_NAME_LEN 16 + +#define SIO_REG_SID 0x20 /* Super I/O ID */ + +#define SIO_REG_SRID 0x27 /* Super I/O Revision */ +#define SIO_REG_IRQ 0x70 /* IRQ number */ +#define SIO_REG_IRQ_TYPE 0x71 /* IRQ type */ + +#define SIO_REG_LDN 0x07 /* Logical Device Number */ +#define SIO_LDN_GPIO 0x07 /* General-Purpose I/O (GPIO) Ports */ +#define SIO_LDN_ACB 0x08 /* Access bus */ + +#define SIO_REG_ACTIVE 0x30 /* Logical Device Activate Register */ +#define SIO_ACTIVE_EN 0x01 /* enabled */ +#define SIO_RESET 0x02 + +#define SIO_REG_IO_MSB 0x60 /* I/O Port Base, bits 15-8 */ +#define SIO_REG_IO_LSB 0x61 /* I/O Port Base, bits 7-0 */ + +#define LDEV_PRIVATE 0xff /* Logical device has non standard dynamic address (like PCI space) */ + +#define LDEV_CLONED (1<<0) + +struct logical_dev +{ + struct list_head ldev_entry; + + atomic_t refcnt; + spinlock_t lock; + + struct list_head chain_list; + spinlock_t chain_lock; + + unsigned char name[SC_NAME_LEN]; + u8 index; + + unsigned long base_addr; + unsigned long range; + + u32 flags; + + void *pdev; + void *orig_ldev; + + u8 irq; + u8 irq_type; + + int (*activate)(void *); + u8 (*read)(void *, int); + void (*write)(void *, int, u8); + void (*control)(void *, int, u8, u8); + irqreturn_t (*irq_handler)(int, void *, struct pt_regs *); +}; + +struct sc_dev +{ + struct list_head sdev_entry; + + atomic_t refcnt; + spinlock_t lock; + + struct list_head chain_list; + spinlock_t chain_lock; + + unsigned char name[SC_NAME_LEN]; + + void *pdev; + unsigned long base_index, base_data; + + struct sc_chip_id *ldevs; + + int (*probe)(void *, unsigned long); + int (*activate_one)(struct logical_dev *); + int (*deactivate_one)(struct logical_dev *); + u8 (*read)(struct logical_dev *, unsigned long); + void (*write)(struct logical_dev *, unsigned long, u8); +}; + +struct sc_chip_id +{ + unsigned char name[SC_NAME_LEN]; + u8 id; +}; + +int sc_add_logical_dev(struct sc_dev *, struct logical_dev *); +void sc_del_logical_dev(struct logical_dev *); +struct logical_dev *sc_get_ldev(char *); +struct logical_dev *sc_get_ldev_in_sdev(char *, struct sc_dev *); +void sc_put_ldev(struct logical_dev *); + +int sc_add_sc_dev(struct sc_dev *); +void sc_del_sc_dev(struct sc_dev *); +struct sc_dev *sc_get_sdev(char *); +void sc_put_sdev(struct sc_dev *); + +struct logical_dev *sc_ldev_clone(struct logical_dev *ldev); +void sc_ldev_unclone(struct logical_dev *ldev); +int sc_ldev_cloned(struct logical_dev *ldev); +int sc_ldev_is_clone(struct logical_dev *ldev); + +struct logical_dev *sc_ldev_alloc(char *name, u8 index); +void sc_ldev_free(struct logical_dev *ldev); + +#endif /* __SC_H */ diff -Nru a/drivers/superio/sc_acb.c b/drivers/superio/sc_acb.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/sc_acb.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,158 @@ +/* + * sc_acb.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include + +#include "sc.h" +#include "sc_acb.h" + +static int sc_acb_activate(void *data); +static u8 sc_acb_read(void *data, int reg); +static void sc_acb_write(void *data, int reg, u8 byte); +static void sc_acb_control(void *data, int pin, u8 mask, u8 ctl); + +static struct logical_dev ldev_acb = { + .name = "ACB", + .index = 0x08, + .range = 16, + + .activate = sc_acb_activate, + .read = sc_acb_read, + .write = sc_acb_write, + .control = sc_acb_control, + + .flags = 0, + .orig_ldev = NULL, +}; + +static void sc_write_reg(struct sc_dev *dev, u8 reg, u8 val) +{ + outb(reg, dev->base_index); + outb(val, dev->base_data); +} + +static u8 sc_read_reg(struct sc_dev *dev, u8 reg) +{ + u8 val; + + outb(reg, dev->base_index); + val = inb(dev->base_data); + + return val; +} + +static int sc_acb_activate(void *data) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + u8 val; + + sc_write_reg(ldev->pdev, ACBCTL2, 1); + + val = sc_read_reg(ldev->pdev, ACBCTL2); + if ((val & 1) != 1) { + printk(KERN_ERR "Can not enable %s at %x: ctl2=%x.\n", + ldev->name, ldev->index, val); + return -ENODEV; + } + + sc_write_reg(ldev->pdev, ACBCTL2, 0x71); + + val = sc_read_reg(ldev->pdev, ACBCTL2); + if (val != 0x71) { + printk(KERN_ERR "ACBCTL2 readback failed: val=%x.\n", val); + return -ENXIO; + } + + sc_write_reg(ldev->pdev, ACBCTL1, + sc_read_reg(ldev->pdev, ACBCTL1) | ACBCTL1_NMINTE); + + val = sc_read_reg(ldev->pdev, ACBCTL1); + if (val) { + printk(KERN_ERR "Disabled, but ACBCTL1=0x%02x\n", val); + return -ENXIO; + } + + sc_write_reg(ldev->pdev, ACBCTL2, + sc_read_reg(ldev->pdev, ACBCTL2) | ACBCTL2_ENABLE); + + sc_write_reg(ldev->pdev, ACBCTL1, + sc_read_reg(ldev->pdev, ACBCTL1) | ACBCTL1_NMINTE); + + val = sc_read_reg(ldev->pdev, ACBCTL1); + if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) { + printk(KERN_ERR "Enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n", + val); + return -ENXIO; + } + + return 0; +} + +static u8 sc_acb_read(void *data, int reg) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + u8 val; + + val = inb(ldev->base_addr + reg); + + //printk("R: %02x\n", val); + + return val; +} + +static void sc_acb_write(void *data, int reg, u8 byte) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + + //printk("W: %02x\n", val); + + outb(byte, ldev->base_addr + reg); +} + +static void sc_acb_control(void *data, int pin, u8 mask, u8 ctl) +{ +} + +static int __devinit sc_acb_init(void) +{ + printk(KERN_INFO "Access Bus logical device driver is activating now.\n"); + INIT_LIST_HEAD(&ldev_acb.ldev_entry); + spin_lock_init(&ldev_acb.lock); + return sc_add_logical_dev(NULL, &ldev_acb); +} + +static void __devexit sc_acb_fini(void) +{ + sc_del_logical_dev(&ldev_acb); + printk(KERN_INFO "Access Bus logical device driver finished.\n"); +} + +module_init(sc_acb_init); +module_exit(sc_acb_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Driver for Access Bus logical device."); diff -Nru a/drivers/superio/sc_acb.h b/drivers/superio/sc_acb.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/sc_acb.h 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,45 @@ +/* + * sc_acb.h + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SC_ACB_H +#define __SC_ACB_H + +#define ACBSDA (ldev->base_addr + 0) +#define ACBST (ldev->base_addr + 1) +#define ACBST_SDAST 0x40 /* SDA Status */ +#define ACBST_BER 0x20 +#define ACBST_NEGACK 0x10 /* Negative Acknowledge */ +#define ACBST_STASTR 0x08 /* Stall After Start */ +#define ACBST_MASTER 0x02 +#define ACBCST (ldev->base_addr + 2) +#define ACBCST_BB 0x02 +#define ACBCTL1 (ldev->base_addr + 3) +#define ACBCTL1_STASTRE 0x80 +#define ACBCTL1_NMINTE 0x40 +#define ACBCTL1_ACK 0x10 +#define ACBCTL1_STOP 0x02 +#define ACBCTL1_START 0x01 +#define ACBADDR (ldev->base_addr + 4) +#define ACBCTL2 (ldev->base_addr + 5) +#define ACBCTL2_ENABLE 0x01 + +#endif /* __SC_ACB_H */ diff -Nru a/drivers/superio/sc_conn.c b/drivers/superio/sc_conn.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/sc_conn.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,124 @@ +/* + * sc_conn.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#include + +#include "sc.h" +#include + +static struct cb_id sc_conn_id = { CN_IDX_SUPERIO, CN_VAL_SUPERIO }; + +static void sc_conn_callback(void *data) +{ + struct cn_msg *reply, *msg = (struct cn_msg *)data; + struct sc_conn_data *rcmd, *cmd = (struct sc_conn_data *)(msg + 1); + struct logical_dev *ldev; + struct sc_dev *sdev; + u8 ret; + + if (msg->len != sizeof(*cmd)) { + printk(KERN_ERR "Wrong additional data size %u, must be %u.\n", + msg->len, sizeof(*cmd)); + return; + } +#if 0 + printk + ("%s: len=%u, seq=%u, ack=%u, sname=%s, lname=%s, idx=0x%x, cmd=%02x [%02x.%02x.%02x].\n", + __func__, msg->len, msg->seq, msg->ack, cmd->sname, cmd->lname, + cmd->idx, cmd->cmd, cmd->p0, cmd->p1, cmd->p2); +#endif + sdev = sc_get_sdev(cmd->sname); + if (!sdev) { + printk(KERN_ERR "%s: sdev %s does not exist.\n", + __func__, cmd->sname); + return; + } + + ldev = sc_get_ldev_in_sdev(cmd->lname, sdev); + if (!ldev) { + printk(KERN_ERR "%s: ldev %s does not exist in chip %s.\n", + __func__, cmd->lname, cmd->sname); + sc_put_sdev(sdev); + return; + } + + ret = 0; + switch (cmd->cmd) { + case SC_CMD_LDEV_READ: + ret = ldev->read(ldev, cmd->p0); + reply = kmalloc(sizeof(*msg) + sizeof(*cmd), GFP_ATOMIC); + if (reply) { + memcpy(reply, msg, sizeof(*reply)); + + /* + * See protocol description in connector.c + */ + reply->ack++; + + rcmd = (struct sc_conn_data *)(reply + 1); + memcpy(rcmd, cmd, sizeof(*rcmd)); + + rcmd->cmd = SC_CMD_LDEV_READ; + rcmd->p0 = cmd->p0; + rcmd->p1 = ret; + + cn_netlink_send(reply, 0); + + kfree(reply); + } else + printk(KERN_ERR "Failed to allocate %d bytes in reply to comamnd 0x%x.\n", + sizeof(*msg) + sizeof(*cmd), cmd->cmd); + break; + case SC_CMD_LDEV_WRITE: + ldev->write(ldev, cmd->p0, cmd->p1); + break; + case SC_CMD_LDEV_CONTROL: + ldev->control(ldev, cmd->p0, cmd->p1, cmd->p2); + break; + case SC_CMD_LDEV_ACTIVATE: + ldev->activate(ldev); + break; + default: + printk(KERN_ERR "Unsupported command 0x%x for %s in chip %s.\n", + cmd->cmd, ldev->name, sdev->name); + break; + } + + sc_put_ldev(ldev); + sc_put_sdev(sdev); +} + +int sc_register_callback(void) +{ + return cn_add_callback(&sc_conn_id, "sc_callback", + (void (*)(void *))&sc_conn_callback); +} + +void sc_unregister_callback(void) +{ + return cn_del_callback(&sc_conn_id); +} + +EXPORT_SYMBOL(sc_register_callback); diff -Nru a/drivers/superio/sc_gpio.c b/drivers/superio/sc_gpio.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/sc_gpio.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,312 @@ +/* + * sc_gpio.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include + +#include "sc.h" +#include "sc_gpio.h" +#include "pc8736x.h" + +static struct gpio_pin gpin[SIO_GPIO_NPINS]; + +static int sc_gpio_activate(void *); +static u8 sc_gpio_read(void *, int); +static void sc_gpio_write(void *, int, u8); +static void sc_gpio_control(void *, int, u8, u8); +static void sc_gpio_pin_select(void *, int); +static irqreturn_t sc_gpio_interrupt(int, void *, struct pt_regs *); + + +static struct logical_dev ldev_gpio = { + .name = "GPIO", + .index = SIO_LDN_GPIO, + .range = 16, + + .activate = sc_gpio_activate, + .read = sc_gpio_read, + .write = sc_gpio_write, + .control = sc_gpio_control, + .irq_handler = sc_gpio_interrupt, + + .irq = 3, + + .flags = 0, + .orig_ldev = NULL, +}; + +static void sc_gpio_write_event(void *data, int pin_number, u8 byte); + +static void sc_write_reg(struct sc_dev *dev, u8 reg, u8 val) +{ + outb(reg, dev->base_index); + outb(val, dev->base_data); +} + +static u8 sc_read_reg(struct sc_dev *dev, u8 reg) +{ + u8 val; + + outb(reg, dev->base_index); + val = inb(dev->base_data); + + return val; +} + +static irqreturn_t sc_gpio_interrupt(int irq, void *data, struct pt_regs * regs) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + static u8 r[4], e[2], s[2]; + u8 cr[4], ce[2], cs[2]; + int i; + + for (i=0; i<2; ++i) + { + ce[i] = inb(ldev->base_addr + i*4 + 2); + cs[i] = inb(ldev->base_addr + i*4 + 3); + } + + for (i=0; i<4; ++i) + cr[i] = inb(ldev->base_addr + i*4 + 1); + + for (i=0; i<4; ++i) + { + u8 p = cr[i] ^ r[i]; + u8 f; + int pin, val; + + if (!p) + continue; + + while((f = ffs(p))) + { + f = ffs(p); + + pin = f + i*8 - 1; + val = ((cr[i] >> (f-1)) & 1); + printk("pin=%2d, val=%1d, jiffies=%lu\n", + pin, val, jiffies); + + p &= ~(1<<(f-1)); + } + + + /* + * Clear status byte. + * Spec does not say that each IRQ shuld be ACKed, + * but it should. + * + * This is probably those ACK. + */ + outb(0xff, ldev->base_addr + i*4 + 3); + } + + memcpy(r, cr, sizeof(r)); + memcpy(s, cs, sizeof(s)); + memcpy(e, ce, sizeof(e)); + + return IRQ_HANDLED; +} + + +static void sc_gpio_pin_select(void *data, int pin_number) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + int port, pin; + u8 val; + + port = pin_number >> 3; + pin = pin_number - (pin_number & (~7)); + val = (port << 4) | pin; + + sc_write_reg(ldev->pdev, SIO_REG_LDN, SIO_LDN_GPIO); + sc_write_reg(ldev->pdev, SIO_GPIO_PINSEL, val); +} + +static int sc_gpio_activate(void *data) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + int i; + + memset(gpin, 0, sizeof(gpin)); + + for (i = 0; i < SIO_GPIO_NPINS; ++i) { + gpin[i].flags = SIO_GPIO_CONF_PULLUP | SIO_GPIO_CONF_EVENT_LEVEL; + gpin[i].mask &= ~(SIO_GPIO_CONF_DEBOUNCE); + + sc_gpio_control(ldev, i, gpin[i].mask, gpin[i].flags); + + gpin[i].state = GPIO_PIN_HIGH; + sc_gpio_write(ldev, i, gpin[i].state); + + sc_gpio_write_event(ldev, i, 1); + } + + outb(0xff, ldev->base_addr + SIO_GPEVEN0); + outb(0xff, ldev->base_addr + SIO_GPEVEN1); + + return 0; +} + +static u8 sc_gpio_read(void *data, int pin_number) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + int port, pin; + u8 val, reg = SIO_GPDI0; + + port = pin_number >> 3; + pin = pin_number - (pin_number & (~7)); + + switch (port) { + case 0: + reg = SIO_GPDI0; + break; + case 1: + reg = SIO_GPDI1; + break; + case 2: + reg = SIO_GPDI2; + break; + case 3: + reg = SIO_GPDI3; + break; + } + + val = inb(ldev->base_addr + reg); + + return ((val >> pin) & 0x01); +} + +static void sc_gpio_write_event(void *data, int pin_number, u8 byte) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + int port, pin; + u8 val, reg = SIO_GPEVEN0; + + port = pin_number >> 3; + pin = pin_number - (pin_number & (~7)); + + switch (port) { + case 0: + reg = SIO_GPEVEN0; + break; + case 1: + reg = SIO_GPEVEN1; + break; + default: + return; + } + + val = inb(ldev->base_addr + reg); + + if (byte) + val |= (1 << pin); + else + val &= ~(1 << pin); + + outb(val, ldev->base_addr + reg); + + outb(1<base_addr + reg+1); +} + +static void sc_gpio_write(void *data, int pin_number, u8 byte) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + int port, pin; + u8 val, reg = SIO_GPDO0, rreg = SIO_GPDI0; + + port = pin_number >> 3; + pin = pin_number - (pin_number & (~7)); + + switch (port) { + case 0: + reg = SIO_GPDO0; + rreg = SIO_GPDI0; + break; + case 1: + reg = SIO_GPDO1; + rreg = SIO_GPDI1; + break; + case 2: + reg = SIO_GPDO2; + rreg = SIO_GPDI2; + break; + case 3: + reg = SIO_GPDO3; + rreg = SIO_GPDI3; + break; + } + + //val = inb(ldev->base_addr + reg); + val = inb(ldev->base_addr + rreg); + + if (byte) + val |= (1 << pin); + else + val &= ~(1 << pin); + + //printk("W: %02x [%d]\n", val, ((val>>pin)&1)); + + outb(val, ldev->base_addr + reg); +} + +static void sc_gpio_control(void *data, int pin, u8 mask, u8 ctl) +{ + struct logical_dev *ldev = (struct logical_dev *)data; + u8 cfg, ev; + + sc_gpio_pin_select(ldev, pin); + + cfg = sc_read_reg(ldev->pdev, SIO_GPIO_PINCFG); + ev = sc_read_reg(ldev->pdev, SIO_GPIO_PINEV); + + cfg &= mask; + cfg |= ctl; + + printk(KERN_INFO "pin=%2d cfg=%02x, mask=%02x, ctl=%02x, event=%02x\n", + pin, cfg, mask, ctl, ev); + + sc_write_reg(ldev->pdev, SIO_GPIO_PINCFG, cfg); +} + +static int __devinit sc_gpio_init(void) +{ + printk(KERN_INFO "GPIO logical device driver is activating now.\n"); + INIT_LIST_HEAD(&ldev_gpio.ldev_entry); + spin_lock_init(&ldev_gpio.lock); + return sc_add_logical_dev(NULL, &ldev_gpio); +} + +static void __devexit sc_gpio_fini(void) +{ + sc_del_logical_dev(&ldev_gpio); + printk(KERN_INFO "GPIO logical device driver finished.\n"); +} + +module_init(sc_gpio_init); +module_exit(sc_gpio_fini); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Driver for GPIO logical device."); diff -Nru a/drivers/superio/sc_gpio.h b/drivers/superio/sc_gpio.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/sc_gpio.h 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,50 @@ +/* + * sc_gpio.h + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SC_GPIO_H +#define __SC_GPIO_H + +#define SIO_GPIO_PINSEL 0xf0 +#define SIO_GPIO_PINCFG 0xf1 +#define SIO_GPIO_PINEV 0xf2 + +#define GPIO_PIN_LOW 0x00 /* low level (logical 0) */ +#define GPIO_PIN_HIGH 0x01 /* high level (logical 1) */ + +#define SIO_GPIO_NPINS 29 + +#define SIO_GPIO_CONF_OUTPUTEN (1 << 0) +#define SIO_GPIO_CONF_PUSHPULL (1 << 1) +#define SIO_GPIO_CONF_PULLUP (1 << 2) +#define SIO_GPIO_CONF_LOCK (1 << 3) +#define SIO_GPIO_CONF_EVENT_LEVEL (1 << 4) +#define SIO_GPIO_CONF_EVENT_POLAR_RIS (1 << 5) +#define SIO_GPIO_CONF_DEBOUNCE (1 << 6) + +struct gpio_pin +{ + u8 state; + u8 flags; + u8 mask; +}; + +#endif /* __SC_GPIO_H */ diff -Nru a/drivers/superio/sc_w1.c b/drivers/superio/sc_w1.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/sc_w1.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,107 @@ +/* + * sc_w1.c + * + * Copyright (c) 2004 Evgeniy Polyakov + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../w1/w1.h" +#include "../w1/w1_int.h" +#include "../w1/w1_log.h" + +#include "../superio/sc.h" +#include "../superio/sc_gpio.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Driver for transport(Dallas 1-wire prtocol) over SuperIO GPIO pins."); + +static int pin_number = 21; /* Use pin 21 by default */ +module_param(pin_number, int, 0); + +struct sc_w1_device { + struct logical_dev *ldev; + struct w1_bus_master bus_master; +} sc_w1; + +static u8 sc_w1_read_bit(unsigned long data) +{ + struct sc_w1_device *swd = (struct sc_w1_device *)data; + + //swd->ldev->control(swd->ldev, pin_number, ~(SIO_GPIO_CONF_PUSHPULL | SIO_GPIO_CONF_PULLUP), 0); + + return swd->ldev->read(swd->ldev, pin_number); +} + +static void sc_w1_write_bit(unsigned long data, u8 bit) +{ + struct sc_w1_device *swd = (struct sc_w1_device *)data; + u8 mask = SIO_GPIO_CONF_OUTPUTEN; + + swd->ldev->control(swd->ldev, pin_number, (bit)?~mask:~0, SIO_GPIO_CONF_PULLUP); + swd->ldev->write(swd->ldev, pin_number, bit); +} + +int __devinit sc_w1_init(void) +{ + int err; + + sc_w1.ldev = sc_get_ldev("GPIO"); + if (!sc_w1.ldev) { + printk(KERN_ERR "Logical device GPIO is not registered.\n"); + return -ENODEV; + } + + sc_w1.bus_master.data = (unsigned long)&sc_w1; + sc_w1.bus_master.read_bit = sc_w1_read_bit; + sc_w1.bus_master.write_bit = sc_w1_write_bit; + + err = w1_add_master_device(&sc_w1.bus_master); + if (err) { + printk(KERN_ERR "Failed to register sc_w1 master device: err=%d.\n", + err); + sc_put_ldev(sc_w1.ldev); + return err; + } + + printk(KERN_INFO "sc_w1 transport driver has been loaded. Pin number %d.\n", + pin_number); + + return 0; +} + +void __devexit sc_w1_fini(void) +{ + w1_remove_master_device(&sc_w1.bus_master); + + sc_put_ldev(sc_w1.ldev); + + printk(KERN_INFO "sc_w1 transport driver has been unloaded.\n"); +} + +module_init(sc_w1_init); +module_exit(sc_w1_fini); diff -Nru a/drivers/superio/scx.c b/drivers/superio/scx.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/scx.c 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,413 @@ +/* + * scx.c + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sc.h" +#include "scx.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Evgeniy Polyakov "); +MODULE_DESCRIPTION("Driver for SCx200/SC1100 SuperIO chips."); + +static int scx200_probe(void *, unsigned long base); +static int scx200_activate_one_logical(struct logical_dev *ldev); +static int scx200_deactivate_one_logical(struct logical_dev *ldev); + +static struct sc_chip_id scx200_logical_devs[] = { + {"RTC", 0x00}, + {"SWC", 0x01}, + {"IRCP", 0x02}, + {"ACB", 0x05}, + {"ACB", 0x06}, + {"SPORT", 0x08}, + {"GPIO", LDEV_PRIVATE}, + {} +}; + +static struct sc_dev scx200_dev = { + .name = "SCx200", + .probe = scx200_probe, + .ldevs = scx200_logical_devs, + .activate_one = scx200_activate_one_logical, + .deactivate_one = scx200_deactivate_one_logical, +}; + +static struct sc_chip_id scx200_sio_ids[] = { + {"SCx200/SC1100", 0xF5}, +}; + +static unsigned long private_base; + +static struct pci_device_id scx200_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE)}, + {PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE)}, + {}, +}; + +MODULE_DEVICE_TABLE(pci, scx200_tbl); + +static int scx200_pci_probe(struct pci_dev *, const struct pci_device_id *); + +static struct pci_driver scx200_pci_driver = { + .name = "scx200", + .id_table = scx200_tbl, + .probe = scx200_pci_probe, +}; + +void scx200_write_reg(struct sc_dev *dev, u8 reg, u8 val) +{ + outb(reg, dev->base_index); + outb(val, dev->base_data); +} + +u8 scx200_read_reg(struct sc_dev *dev, u8 reg) +{ + u8 val; + + outb(reg, dev->base_index); + val = inb(dev->base_data); + + return val; +} + +static u8 scx200_gpio_read(void *data, int pin_number) +{ + u32 val; + int bank; + unsigned long base; + struct logical_dev *ldev = (struct logical_dev *)data; + + if (pin_number > 63 || pin_number < 0) + return 0; + + bank = 0; + base = ldev->base_addr; + + if (pin_number > 31) { + bank = 1; + base = ldev->base_addr + 0x10; + pin_number -= 32; + } + + val = inl(base + 0x04); + + return ((val >> pin_number) & 0x01); +} + +static void scx200_gpio_write(void *data, int pin_number, u8 byte) +{ + u32 val; + int bank; + unsigned long base; + struct logical_dev *ldev = (struct logical_dev *)data; + + if (pin_number > 63 || pin_number < 0) + return; + + bank = 0; + base = ldev->base_addr; + + if (pin_number > 31) { + bank = 1; + base = ldev->base_addr + 0x10; + pin_number -= 32; + } + + val = inl(base); + + if (byte) + val |= (1 << pin_number); + else + val &= ~(1 << pin_number); + + outl(val, base); +} + +void scx200_gpio_control(void *data, int pin_number, u8 mask, u8 ctl) +{ + u32 val; + int bank; + unsigned long base; + struct logical_dev *ldev = (struct logical_dev *)data; + + if (pin_number > 63 || pin_number < 0) + return; + + bank = 0; + base = ldev->base_addr; + + if (pin_number > 31) { + bank = 1; + base = ldev->base_addr + 0x10; + pin_number -= 32; + } + + /* + * Pin selection. + */ + + val = 0; + val = ((bank & 0x01) << 5) | pin_number; + outl(val, ldev->base_addr + 0x20); + + val = inl(ldev->base_addr + 0x24); + + val &= 0x7f; + val &= mask; + val |= ctl; + + outl(val, ldev->base_addr + 0x24); +} + +int scx200_gpio_activate(void *data) +{ + return 0; +} + +static int scx200_ldev_index_by_name(char *name) +{ + int i; + + for (i = 0; + i < sizeof(scx200_logical_devs) / sizeof(scx200_logical_devs[0]); ++i) + if (!strncmp(scx200_logical_devs[i].name, name, SC_NAME_LEN)) + return i; + + return -ENODEV; +} + +static int scx200_chip_index(u8 id) +{ + int i; + + for (i = 0; i < sizeof(scx200_sio_ids) / sizeof(scx200_sio_ids[0]); ++i) + if (scx200_sio_ids[i].id == id) + return i; + + return -ENODEV; +} + +static int scx200_probe(void *data, unsigned long base) +{ + unsigned long size = 2; + u8 id; + int chip_num; + struct sc_dev *dev = (struct sc_dev *)data; + + /* + * Special address to handle. + */ + if (base == 0) { + return scx200_probe(data, 0x015C); + } + + dev->base_index = base; + dev->base_data = base + 1; + + id = scx200_read_reg(dev, SIO_REG_SID); + chip_num = scx200_chip_index(id); + + if (chip_num >= 0) { + printk(KERN_INFO "Found %s [0x%x] at 0x%04lx-0x%04lx.\n", + scx200_sio_ids[chip_num].name, + scx200_sio_ids[chip_num].id, base, base + size - 1); + return 0; + } + + printk(KERN_INFO "Found nothing at 0x%04lx-0x%04lx.\n", + base, base + size - 1); + + return -ENODEV; +} + +static int scx200_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + private_base = pci_resource_start(pdev, 0); + printk(KERN_INFO "%s: GPIO base 0x%lx.\n", pci_name(pdev), private_base); + + if (!request_region + (private_base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO")) { + printk(KERN_ERR "%s: failed to request %d bytes I/O region for GPIOs.\n", + pci_name(pdev), SCx200_GPIO_SIZE); + return -EBUSY; + } + + pci_set_drvdata(pdev, &private_base); + pci_enable_device(pdev); + + return 0; +} + +static int scx200_deactivate_one_logical(struct logical_dev *ldev) +{ + if (ldev->index != LDEV_PRIVATE) + return -ENODEV; + + private_base -= 0x10; + + return 0; +} + +static int scx200_find_private_device(struct logical_dev *ldev) +{ + struct sc_dev *dev = (struct sc_dev *)ldev->pdev; + + /* + * SCx200/SC1100 has only GPIO in it's private space. + */ + + if (strncmp(ldev->name, "GPIO", SC_NAME_LEN)) { + printk(KERN_ERR "Logical device %s at private space is not supported in chip %s.\n", + ldev->name, dev->name); + return -ENODEV; + } + + ldev->base_addr = private_base; + private_base += 0x10; + + ldev->read = scx200_gpio_read; + ldev->write = scx200_gpio_write; + ldev->control = scx200_gpio_control; + ldev->activate = scx200_gpio_activate; + + return 0; +} + +static int scx200_activate_one_logical(struct logical_dev *ldev) +{ + int err, idx; + struct sc_dev *dev = ldev->pdev; + u8 active; + + idx = scx200_ldev_index_by_name(ldev->name); + if (idx < 0) { + printk(KERN_INFO "Chip %s does not have logical device %s at %x.\n", + dev->name, ldev->name, ldev->index); + return -ENODEV; + } + + if (scx200_logical_devs[idx].id == LDEV_PRIVATE) { + err = scx200_find_private_device(ldev); + if (err) + return err; + + printk(KERN_INFO "\t%16s - found at 0x%lx.\n", + ldev->name, ldev->base_addr); + } else { + scx200_write_reg(dev, SIO_REG_LDN, ldev->index); + active = scx200_read_reg(dev, SIO_REG_ACTIVE); + if ((active & SIO_ACTIVE_EN) == 0) { + printk(KERN_INFO "\t%16s - not activated at %x: activating... ", + ldev->name, ldev->index); + + scx200_write_reg(dev, SIO_REG_ACTIVE, + active | SIO_ACTIVE_EN); + active = scx200_read_reg(dev, SIO_REG_ACTIVE); + if ((active & SIO_ACTIVE_EN) == 0) { + printk("failed.\n"); + return -ENODEV; + } + printk("done.\n"); + } + + ldev->base_addr = scx200_read_reg(dev, SIO_REG_IO_LSB); + ldev->base_addr |= (scx200_read_reg(dev, SIO_REG_IO_MSB) << 8); + } + + err = ldev->activate(ldev); + if (err < 0) { + printk(KERN_INFO "\t%16s - not activated: ->activate() failed with error code %d.\n", + ldev->name, err); + return -ENODEV; + } + + printk(KERN_INFO "\t%16s - activated at %x: 0x%04lx-0x%04lx\n", + ldev->name, ldev->index, ldev->base_addr, + ldev->base_addr + ldev->range); + + return 0; +} + +static int scx200_init(void) +{ + int err; + + err = pci_module_init(&scx200_pci_driver); + if (err) { + printk(KERN_ERR "Failed to register PCI driver for device %s : err=%d.\n", + scx200_pci_driver.name, err); + return err; + } + + err = sc_add_sc_dev(&scx200_dev); + if (err) + return err; + + printk(KERN_INFO "Driver for %s SuperIO chip.\n", scx200_dev.name); + return 0; +} + +static void scx200_fini(void) +{ + sc_del_sc_dev(&scx200_dev); + + while (atomic_read(&scx200_dev.refcnt)) + { + printk(KERN_INFO "Waiting for %s to became free: refcnt=%d.\n", + scx200_dev.name, atomic_read(&scx200_dev.refcnt)); + + msleep_interruptible(1000); + + if (signal_pending(current)) + flush_signals(current); + + if (current->flags & PF_FREEZE) + refrigerator(PF_FREEZE); + } + + pci_unregister_driver(&scx200_pci_driver); + if (private_base) + release_region(private_base, SCx200_GPIO_SIZE); +} + +module_init(scx200_init); +module_exit(scx200_fini); + +EXPORT_SYMBOL(scx200_write_reg); +EXPORT_SYMBOL(scx200_read_reg); diff -Nru a/drivers/superio/scx.h b/drivers/superio/scx.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/superio/scx.h 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,28 @@ +/* + * scx.h + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SCX200_H +#define __SCX200_H + +#define SCx200_GPIO_SIZE 0x2c + +#endif /* __SCX200_H */ diff -Nru a/include/linux/connector.h b/include/linux/connector.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/include/linux/connector.h 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,150 @@ +/* + * connector.h + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __CONNECTOR_H +#define __CONNECTOR_H + +#include + +#define CN_IDX_KOBJECT_UEVENT 0xabcd /* Kobject's userspace events*/ +#define CN_VAL_KOBJECT_UEVENT 0x0000 +#define CN_IDX_SUPERIO 0xaabb /* SuperIO subsystem */ +#define CN_VAL_SUPERIO 0xccdd + + +#define CONNECTOR_MAX_MSG_SIZE 1024 + +struct cb_id +{ + __u32 idx; + __u32 val; +}; + +struct cn_msg +{ + struct cb_id id; + + __u32 seq; + __u32 ack; + + __u32 len; /* Length of the following data */ + __u8 data[0]; +}; + +struct cn_notify_req +{ + __u32 first; + __u32 range; +}; + +struct cn_ctl_msg +{ + __u32 idx_notify_num; + __u32 val_notify_num; + __u32 group; + __u32 len; + __u8 data[0]; +}; + + +#ifdef __KERNEL__ + +#include + +#include +#include + +#include + +#define CN_CBQ_NAMELEN 32 + +struct cn_queue_dev +{ + atomic_t refcnt; + unsigned char name[CN_CBQ_NAMELEN]; + + struct workqueue_struct *cn_queue; + + struct list_head queue_list; + spinlock_t queue_lock; + + int netlink_groups; + struct sock *nls; +}; + +struct cn_callback +{ + unsigned char name[CN_CBQ_NAMELEN]; + + struct cb_id id; + void (* callback)(void *); + void *priv; + + atomic_t refcnt; +}; + +struct cn_callback_entry +{ + struct list_head callback_entry; + struct cn_callback *cb; + struct work_struct work; + struct cn_queue_dev *pdev; + + void (* destruct_data)(void *); + void *ddata; + + int seq, group; + struct sock *nls; +}; + +struct cn_ctl_entry +{ + struct list_head notify_entry; + struct cn_ctl_msg *msg; +}; + +struct cn_dev +{ + struct cb_id id; + + u32 seq, groups; + struct sock *nls; + void (*input)(struct sock *sk, int len); + + struct cn_queue_dev *cbdev; +}; + +extern int cn_already_initialized; + +int cn_add_callback(struct cb_id *, char *, void (* callback)(void *)); +void cn_del_callback(struct cb_id *); +void cn_netlink_send(struct cn_msg *, u32); + +int cn_queue_add_callback(struct cn_queue_dev *dev, struct cn_callback *cb); +void cn_queue_del_callback(struct cn_queue_dev *dev, struct cn_callback *cb); + +struct cn_queue_dev *cn_queue_alloc_dev(char *name, struct sock *); +void cn_queue_free_dev(struct cn_queue_dev *dev); + +int cn_cb_equal(struct cb_id *, struct cb_id *); + +#endif /* __KERNEL__ */ +#endif /* __CONNECTOR_H */ diff -Nru a/include/linux/i2c.h b/include/linux/i2c.h --- a/include/linux/i2c.h 2005-03-23 19:38:41 -08:00 +++ b/include/linux/i2c.h 2005-03-23 19:38:41 -08:00 @@ -306,9 +306,6 @@ #define ANY_I2C_BUS 0xffff #define ANY_I2C_ISA_BUS 9191 -/* The length of the option lists */ -#define I2C_CLIENT_MAX_OPTS 48 - /* ----- functions exported by i2c.o */ @@ -526,6 +523,9 @@ #define I2C_MAJOR 89 /* Device major number */ /* These defines are used for probing i2c client addresses */ +/* The length of the option lists */ +#define I2C_CLIENT_MAX_OPTS 48 + /* Default fill of many variables */ #define I2C_CLIENT_DEFAULTS {I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ @@ -544,19 +544,12 @@ I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END} -/* This is ugly. We need to evaluate I2C_CLIENT_MAX_OPTS before it is - stringified */ -#define I2C_CLIENT_MODPARM_AUX1(x) "1-" #x "h" -#define I2C_CLIENT_MODPARM_AUX(x) I2C_CLIENT_MODPARM_AUX1(x) -#define I2C_CLIENT_MODPARM I2C_CLIENT_MODPARM_AUX(I2C_CLIENT_MAX_OPTS) - /* I2C_CLIENT_MODULE_PARM creates a module parameter, and puts it in the module header */ #define I2C_CLIENT_MODULE_PARM(var,desc) \ static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; \ static unsigned int var##_num; \ - /*MODULE_PARM(var,I2C_CLIENT_MODPARM);*/ \ module_param_array(var, short, &var##_num, 0); \ MODULE_PARM_DESC(var,desc) diff -Nru a/include/linux/sc_conn.h b/include/linux/sc_conn.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/include/linux/sc_conn.h 2005-03-23 19:38:41 -08:00 @@ -0,0 +1,50 @@ +/* + * sc_conn.h + * + * 2004 Copyright (c) Evgeniy Polyakov + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SC_CONN_H +#define __SC_CONN_H + +enum sc_conn_cmd +{ + SC_CMD_LDEV_READ = 0, + SC_CMD_LDEV_WRITE, + SC_CMD_LDEV_CONTROL, + SC_CMD_LDEV_ACTIVATE, + + __SC_CMD_MAX_CMD_NUMBER +}; + +struct sc_conn_data +{ + char sname[16]; + char lname[16]; + __u32 idx; + + __u8 cmd; + __u8 p0, p1, p2; + + __u8 data[0]; +}; + +int sc_register_callback(void); +void sc_unregister_callback(void); + +#endif /* __SC_CONN_H */ diff -Nru a/lib/kobject_uevent.c b/lib/kobject_uevent.c --- a/lib/kobject_uevent.c 2005-03-23 19:38:41 -08:00 +++ b/lib/kobject_uevent.c 2005-03-23 19:38:41 -08:00 @@ -12,6 +12,7 @@ * Kay Sievers * Arjan van de Ven * Greg Kroah-Hartman + * Evgeniy Polyakov */ #include @@ -21,6 +22,7 @@ #include #include #include +#include #include #define BUFFER_SIZE 1024 /* buffer for the hotplug env */ @@ -53,6 +55,70 @@ #ifdef CONFIG_KOBJECT_UEVENT static struct sock *uevent_sock; +#ifdef CONFIG_CONNECTOR +static struct cb_id uid = {CN_IDX_KOBJECT_UEVENT, CN_VAL_KOBJECT_UEVENT}; +static void kobject_uevent_connector_callback(void *data) +{ +} + +static void kobject_uevent_send_connector(const char *signal, const char *obj, char **envp, int gfp_mask) +{ + if (cn_already_initialized) { + int size; + struct cn_msg *msg; + static int uevent_connector_initialized; + + if (!uevent_connector_initialized) { + cn_add_callback(&uid, "kobject_uevent", kobject_uevent_connector_callback); + uevent_connector_initialized = 1; + } + + + size = strlen(signal) + strlen(obj) + 2 + BUFFER_SIZE + sizeof(*msg); + msg = kmalloc(size, gfp_mask); + if (msg) { + u8 *pos; + int len; + + memset(msg, 0, size); + + msg->len = size - sizeof(*msg); + + memcpy(&msg->id, &uid, sizeof(msg->id)); + + size -= sizeof(*msg); + + pos = (u8 *)(msg + 1); + + len = snprintf(pos, size, "%s@%s", signal, obj); + len++; + size -= len; + pos += len; + + if (envp) { + int i; + + for (i = 2; envp[i]; i++) { + len = strlen(envp[i]) + 1; + snprintf(pos, size, "%s", envp[i]); + size -= len; + pos += len; + } + } + + cn_netlink_send(msg, 0); + + kfree(msg); + } + } +} +#else +static void kobject_uevent_send_connector(const char *signal, const char *obj, char **envp, int gfp_mask) +{ +} +#endif + + /** * send_uevent - notify userspace by sending event trough netlink socket * @@ -92,6 +158,8 @@ strcpy(pos, envp[i]); } } + + kobject_uevent_send_connector(signal, obj, envp, gfp_mask); return netlink_broadcast(uevent_sock, skb, 0, 1, gfp_mask); }