bk://kernel.bkbits.net/gregkh/linux/i2c-2.6 mgreer@mvista.com[gregkh]|ChangeSet|20050317065837|58583 mgreer # This is a BitKeeper generated diff -Nru style patch. # # 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/drivers/Kconfig b/drivers/Kconfig --- a/drivers/Kconfig 2005-03-20 16:43:51 -08:00 +++ b/drivers/Kconfig 2005-03-20 16:43:51 -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-20 16:43:51 -08:00 +++ b/drivers/Makefile 2005-03-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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/busses/Kconfig b/drivers/i2c/busses/Kconfig --- a/drivers/i2c/busses/Kconfig 2005-03-20 16:43:51 -08:00 +++ b/drivers/i2c/busses/Kconfig 2005-03-20 16:43:51 -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-20 16:43:51 -08:00 +++ b/drivers/i2c/busses/i2c-elektor.c 2005-03-20 16:43:51 -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-ite.c b/drivers/i2c/busses/i2c-ite.c --- a/drivers/i2c/busses/i2c-ite.c 2005-03-20 16:43:51 -08:00 +++ b/drivers/i2c/busses/i2c-ite.c 2005-03-20 16:43:51 -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/chips/Kconfig b/drivers/i2c/chips/Kconfig --- a/drivers/i2c/chips/Kconfig 2005-03-20 16:43:51 -08:00 +++ b/drivers/i2c/chips/Kconfig 2005-03-20 16:43:51 -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-20 16:43:51 -08:00 +++ b/drivers/i2c/chips/Makefile 2005-03-20 16:43:51 -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-20 16:43:51 -08:00 +++ b/drivers/i2c/chips/adm1021.c 2005-03-20 16:43:51 -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-20 16:43:51 -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/it87.c b/drivers/i2c/chips/it87.c --- a/drivers/i2c/chips/it87.c 2005-03-20 16:43:51 -08:00 +++ b/drivers/i2c/chips/it87.c 2005-03-20 16:43:51 -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/lm90.c b/drivers/i2c/chips/lm90.c --- a/drivers/i2c/chips/lm90.c 2005-03-20 16:43:51 -08:00 +++ b/drivers/i2c/chips/lm90.c 2005-03-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -08:00 +++ b/drivers/i2c/chips/m41t00.c 2005-03-20 16:43:51 -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-20 16:43:51 -08:00 +++ b/drivers/i2c/chips/w83627hf.c 2005-03-20 16:43:51 -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-20 16:43:51 -08:00 +++ b/drivers/i2c/i2c-core.c 2005-03-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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-20 16:43:51 -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/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-20 16:43:51 -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-20 16:43:51 -08:00 +++ b/lib/kobject_uevent.c 2005-03-20 16:43:51 -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); }