Delete all kernel bus drivers, documentation and references. We are

moving to a user-space-only package, all the drivers are maintained in
the Linux kernel tree itself.


git-svn-id: http://lm-sensors.org/svn/lm-sensors/branches/lm-sensors-3.0.0@4386 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Jean Delvare committed 2007-05-05 12:43:03 +00:00
1 parent adb328263c
commit b170e0be99
56 files changed
+10 -12595

No files matched your search

-8
View File
@@ -77,11 +77,3 @@ lock bit for the base address registers.
Also, the temperature reading in sis5595 is usually wrong.
Different formulas are required for different versions of
the chip and different motherboards.
================================
bus drivers (all)
Rmmod'ing a bus driver when the /proc directory for a chip
driver on that bus is in use may cause a kernel oops, because
the chip driver gets removed too. See ticket #331 for details.
Not likely to happen. No obvious way to fix.
+1
View File
@@ -2,6 +2,7 @@ lm_sensors CHANGES file
-----------------------
SVN HEAD
All bus modules and documentation: Delete
File doc/version-2: Delete, obsolete
File sensors.conf.eg: Add a dme1737 section
File lm_sensors.sysconfig: Delete, now generated by sensors-detect
+1 -3
View File
@@ -176,7 +176,7 @@ ifneq ($(MAKECMDGOALS),user_uninstall)
ifneq ($(MAKECMDGOALS),package)
ifneq ($(MAKECMDGOALS),userpackage)
ifneq ($(MAKECMDGOALS),manhtml)
SRCDIRS += kernel kernel/busses kernel/chips
SRCDIRS += kernel kernel/chips
endif
endif
endif
@@ -367,9 +367,7 @@ userpackage: version clean $(KERNELINCLUDEDIR)/sensors.h
find $$lmpackage/ -type f | grep -v ^$$lmpackage/$$lmpackage$$ | \
grep -v ^$$lmpackage/$$lmpackage.tar$$ | \
grep -v ^$$lmpackage/doc/chips | \
grep -v ^$$lmpackage/doc/busses | \
grep -v ^$$lmpackage/kernel/chips | \
grep -v ^$$lmpackage/kernel/busses | \
grep -v ^$$lmpackage/$$ | \
grep -v /CVS | \
grep -v /\\.# | \
-21
View File
@@ -42,27 +42,6 @@ See the lm_sensors download page for further guidance:
=============================================================================
At least the following I2C/SMBus adapters are supported:
Acer Labs M1533, M1535, M1543C and M1563
AMD 756, 766, 768 and 8111
AMD 8111 SMBus 2.0
Apple Hydra (used on some PPC machines)
ATI IXP200, IXP300, IXP400, SB600
DEC 21272/21274 (Tsunami/Typhoon - on Alpha boards)
Intel ICH/ICH0/ICH2/ICH3/ICH4/ICH5/ICH6/ICH7/ICH8/ICH9 (82801xx)
Intel 6300ESB, 631xESB/632xESB (ESB2)
Intel PIIX4 (used in many Intel chipsets)
Intel I810/I810E/I815/I845G GMCH
Intel 82443MX (440MX)
NVidia nForce, nForce2, nForce3, nForce4
ServerWorks OSB4, CSB5, CSB6, HT-1000
SiS 5595, 630, 645, 655, 730
SMSC Victory66
3Dfx Voodoo 3 and Banshee
VIA Technologies VT82C586B, VT82C596A/B, VT82C686A/B, VT8231,
VT8233, VT8233A, VT8235, VT8237R/A, VT8251, CX700
At least the following hardware sensor chips are supported:
Analog Devices ADM1021, ADM1021A, ADM1022, ADM1023, ADM1024,
ADM1025, ADM1026, ADM1027, ADM1030, ADM1031,
+1 -1
View File
@@ -5,7 +5,7 @@ The directories within this package:
* etc
A sample configuration file for the 'sensors' program.
* kernel
The kernel modules: I2C/SMBus master drivers, and sensor chip drivers.
The kernel modules: sensor chip drivers.
* lib
The user-space sensors support library code.
* prog
-53
View File
@@ -1,53 +0,0 @@
Kernel driver `i2c-ali1535.o'
Status: Beta; block reads/writes untested (not used by any current chip
driver)
Supported adapters:
* Acer Labs, Inc. ALI 1535 (south bridge)
Datasheet: Available at the Acer Labs website (password may be required)
Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
<phil@netroedge.com>, Mark D. Studebaker <mdsxyz123@yahoo.com> and
Dan Eaton <dan.eaton@rocketlogix.com>
License: GPL
Module Parameters
-----------------
(none)
Description
-----------
This is the driver for the SMB Host controller on
Acer Labs Inc. (ALI) M1535 South Bridge.
The M1535 is a South bridge for portable systems.
It is very similar to the M15x3 South bridges also produced
by Acer Labs Inc. Some of the registers within the part
have moved and some have been redefined slightly. Additionally,
the sequencing of the SMBus transactions has been modified
to be more consistent with the sequencing recommended by
the manufacturer and observed through testing. These
changes are reflected in this driver and can be identified
by comparing this driver to the i2c-ali15x3 driver.
For an overview of these chips see http://www.acerlabs.com
The SMB controller is part of the 7101 device, which is an
ACPI-compliant Power Management Unit (PMU).
The whole 7101 device has to be enabled for the SMB to work.
You can't just enable the SMB alone.
The SMB and the ACPI have separate I/O spaces.
We make sure that the SMB is enabled. We leave the ACPI alone.
Features
--------
This driver controls the SMB Host only.
This driver does not use interrupts.
-135
View File
@@ -1,135 +0,0 @@
Kernel driver `i2c-ali15x3.o'
Status: Complete and tested; block reads/writes untested (not used by any
current chip driver)
Supported adapters:
* Acer Labs, Inc. ALI 1533 and 1543C (south bridge)
Datasheet: Available at the Acer Labs website (password required)
Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
<phil@netroedge.com>, and Mark D. Studebaker <mdsxyz123@yahoo.com>
License: GPL
Module Parameters
-----------------
* force_addr: int
Initialize the base address of the i2c controller
Notes
-----
The force_addr parameter is useful for boards that don't set the address
in the BIOS. Does not do a PCI force; the device must still be present
in lspci. Don't use this unless the driver complains that the
base address is not set.
Example: 'modprobe i2c-ali15x3 force_addr=0xe800'
SMBus periodically hangs on ASUS P5A motherboards and can only be
cleared by a power cycle. Cause unknown (see Issues below).
Description
-----------
This is the driver for the SMB Host controller on
Acer Labs Inc. (ALI) M1541 and M1543C South Bridges.
The M1543C is a South bridge for desktop systems.
The M1541 is a South bridge for portable systems.
They are part of the following ALI chipsets:
"Aladdin Pro 2": Includes the M1621 Slot 1 North bridge
with AGP and 100MHz CPU Front Side bus
"Aladdin V": Includes the M1541 Socket 7 North bridge
with AGP and 100MHz CPU Front Side bus
Some Aladdin V motherboards:
Asus P5A
Atrend ATC-5220
BCM/GVC VP1541
Biostar M5ALA
Gigabyte GA-5AX (** Generally doesn't work because the BIOS doesn't
enable the 7101 device! **)
Iwill XA100 Plus
Micronics C200
Microstar (MSI) MS-5169
"Aladdin IV": Includes the M1541 Socket 7 North bridge
with host bus up to 83.3 MHz.
For an overview of these chips see http://www.acerlabs.com
At this time the full data sheets on the web site are password protected,
however if you contact the ALI office in San Jose
they may give you the password.
The M1533/M1543C devices appear as FOUR separate devices
on the PCI bus. An output of lspci will show something similar
to the following:
00:02.0 USB Controller: Acer Laboratories Inc. M5237 (rev 03)
00:03.0 Bridge: Acer Laboratories Inc. M7101 <= THIS IS THE ONE WE NEED
00:07.0 ISA bridge: Acer Laboratories Inc. M1533 (rev c3)
00:0f.0 IDE interface: Acer Laboratories Inc. M5229 (rev c1)
** IMPORTANT **
** If you have a 1533 or 1543C on the board and you get
** "ali15x3.o: Error: Can't detect ali15x3!"
** then run lspci.
** If you see the 1533 and 5229 devices but NOT the 7101 device,
** then you must enable ACPI, the PMU, SMB, or something similar
** in the BIOS. If you can't figure out how to enable the
** 7101 device in the BIOS, you can try one of the "hotplug" modules -
** see below.
** The driver won't work if it can't find the 7101 device.
The SMB controller is part of the 7101 device, which is an
ACPI-compliant Power Management Unit (PMU).
The whole 7101 device has to be enabled for the SMB to work.
You can't just enable the SMB alone.
The SMB and the ACPI have separate I/O spaces.
We make sure that the SMB is enabled. We leave the ACPI alone.
Features
--------
This driver controls the SMB Host only.
The SMB Slave controller on the M15X3 is not enabled.
This driver does not use interrupts.
Issues
------
This driver requests the I/O space for only the SMB
registers. It doesn't use the ACPI region.
On the ASUS P5A motherboard, there are several reports that
the SMBus will hang and this can only be resolved by
powering off the computer. It appears to be worse when the board
gets hot, for example under heavy CPU load, or in the summer.
There may be electrical problems on this board.
On the P5A, the W83781D sensor chip is on both the ISA and
SMBus. Therefore the SMBus hangs can generally be avoided
by accessing the W83781D on the ISA bus only.
Hot Plug Modules
----------------
There are EXPERIMENTAL "hotplug" modules which will enable the
m7101 device if it is not enabled on your motherboard.
Use these at your own risk. Read the README file carefully.
m7101 Version 0.1 for 2.2 and early 2.3 kernels:
README: http://www.netroedge.com/~lm78/misc/README-m7101.txt
source and README: http://www.netroedge.com/~lm78/misc/m7101-0.1.tar.gz
m7101 Version 0.2 for 2.4 and later 2.3 kernels:
In the directory prog/hotplug in this package.
Must be built separately.
-30
View File
@@ -1,30 +0,0 @@
Kernel driver `i2c-amd756.o'
Status: Complete and somewhat tested
Supported adapters:
* AMD 756
* AMD 766
* AMD 768
* AMD 8111
Datasheets: Publicly available on AMD website
* nVidia nForce
Datasheet: Unavailable
Module Parameters
-----------------
(none)
Description
-----------
This driver supports the AMD 756, 766, 768 and 8111 Peripheral Bus Controllers, and
the nVidia nForce.
Note that for the 8111, there are two SMBus adapters. The SMBus 1.0 adapter is
supported by this driver, and the SMBus 2.0 adapter is supported
by the i2c-amd8111 driver.
-48
View File
@@ -1,48 +0,0 @@
Kernel driver `i2c-adm8111.o'
Status: Slightly tested.
Supported adapters:
* AMD-8111 SMBus 2.0 PCI interface
Datasheets:
AMD datasheet not yet available, but almost everything can be found
in the publicly available ACPI 2.0 specification, which the adapter
follows.
Author: Vojtech Pavlik <vojtech@suse.cz>
Module Parameters
-----------------
None.
Description
-----------
If you see something like this:
00:07.2 SMBus: Advanced Micro Devices [AMD] AMD-8111 SMBus 2.0 (rev 02)
Subsystem: Advanced Micro Devices [AMD] AMD-8111 SMBus 2.0
Flags: medium devsel, IRQ 19
I/O ports at d400 [size=32]
in your 'lspci -v', then this driver is for your chipset.
Process Call Support
--------------------
Supported.
SMBus 2.0 Support
-----------------
Supported. Both PEC and block process call support is implemented.
Slave mode or host notification are not yet implemented.
Notes
-----
Note that for the 8111, there are two SMBus adapters. The SMBus 2.0 adapter is
supported by this driver, and the SMBus 1.0 adapter is supported
by the i2c-amd756 driver.
-35
View File
@@ -1,35 +0,0 @@
Kernel driver `i2c-hydra.o'
Status: Complete and somewhat tested
Supported adapters:
* Apple `Hydra' Mac I/O
Datasheet: Was publicly available from the Apple website, now seems to
have disappeared?!?
Author: Geert Uytterhoeven <geert@linux-m68k.org>
Module Parameters
-----------------
(none)
Description
-----------
i2c-hydra is an i2c bus driver for CHRP motherboards with the Apple Hydra Mac
I/O.
Your /proc/pci or lspci listing must show this :
Unknown class: Apple Hydra (rev 2)
To install this driver, simply do
# modprobe i2c-hydra
This driver uses the i2c-algo-bit module which must be
modprobed and/or enabled in your kernel
(Character Devices -> I2C Support -> I2C Bit-Banging Interfaces).
-107
View File
@@ -1,107 +0,0 @@
Kernel driver `i2c-i801.o'
Status: Tested and stable.
Block reads/writes lightly tested.
HW PEC support Beta.
I2C Block Read support Beta.
Supported adapters:
* Intel 82801AA and 82801AB (ICH and ICH0 - part of the
'810' and '810E' chipsets)
* Intel 82801BA (ICH2 - part of the '815E' chipset)
* Intel 82801CA/CAM (ICH3)
* Intel 82801DB (ICH4)
* Intel 82801EB/ER (ICH5)
* Intel 6300ESB/ESB2
* Intel 82801FB/FR/FW/FRW (ICH6)
* Intel ICH7/ICH8/ICH9
Datasheets: Publicly available at the Intel website
Authors: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
<phil@netroedge.com>, and Mark Studebaker <mdsxyz123@yahoo.com>
Module Parameters
-----------------
* force_addr: int
Forcibly enable the ICH at the given address. EXTREMELY DANGEROUS!
Description
-----------
The ICH (properly known as the 82801AA), ICH0 (82801AB),
ICH2 (82801BA), ICH3 (82801CA/CAM) and later devices
are Intel chips that are a part of
Intel's '810' chipset for Celeron-based PCs,
'810E' chipset for Pentium-based PCs, '815E' chipset, and others.
The ICH chips contain at least SEVEN separate PCI functions
in TWO logical PCI devices.
An output of lspci will show something similar to the following:
00:1e.0 PCI bridge: Intel Corporation: Unknown device 2418 (rev 01)
00:1f.0 ISA bridge: Intel Corporation: Unknown device 2410 (rev 01)
00:1f.1 IDE interface: Intel Corporation: Unknown device 2411 (rev 01)
00:1f.2 USB Controller: Intel Corporation: Unknown device 2412 (rev 01)
00:1f.3 Unknown class [0c05]: Intel Corporation: Unknown device 2413 (rev 01)
The SMBus controller is function 3 in device 1f.
Class 0c05 is SMBus Serial Controller.
If you do NOT see the 24x3 device at function 3, and you can't
figure out any way in the BIOS to enable it,
(and especially if you have an Asus P4B board),
see prog/hotplug/README.p4b.
The ICH chips are quite similar to Intel's PIIX4 chip,
at least in the SMBus controller.
See the file i2c-piix4 for some additional information.
Process Call Support
--------------------
Not supported.
I2C Block Read Support
----------------------
The ICH4 supports a special 3-byte address (command code
plus 2 more bytes) I2C block read.
The driver and the kernel i2c protocol stack do not
support this.
The ICH5 and higher support a standard 1-byte address (command code)
I2C block read. The driver does support this.
SMBus 2.0 Support
-----------------
The 82801DB (ICH4) and later chips support several SMBus 2.0 features.
Kernel and driver support are as follows:
Feature Kernel Driver
Slave mode no no
Host notify no no
Block PEC yes yes
Hardware PEC yes yes
Other ICH4 and later Features
-----------------------------
The following additional features are also _not_ supported:
32 Byte buffer
**********************
The lm_sensors project gratefully acknowledges the support of
Texas Instruments in the initial development of this driver.
The lm_sensors project gratefully acknowledges the support of
Intel in the development of SMBus 2.0 / ICH4 features of this driver.
-66
View File
@@ -1,66 +0,0 @@
Kernel driver `i2c-i810.o'
Status: Stable
Supported adapters:
* Intel 82810, 82810-DC100, 82810E, and 82815 (GMCH)
* Intel 82845G (GMCH)
Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
<phil@netroedge.com>,
Kyösti Mälkki <kmalkki@cc.hut.fi>,
and Mark D. Studebaker <mdsxyz123@yahoo.com>
Main contact: Mark Studebaker <mdsxyz123@yahoo.com>
Module Parameters
-----------------
(none)
Description
-----------
This driver uses the i2c-algo-bit module which must be
modprobed and/or enabled in your kernel
(Character Devices -> I2C Support -> I2C Bit-Banging Interfaces).
WARNING: If you have an '810' or '815' motherboard, your standard I2C
temperature sensors are most likely on the 801's I2C bus.
You want the i2c-i801.o driver for those, not this driver.
Now for the i2c-i810...
The GMCH chip contains two I2C interfaces.
The first interface is used for DDC (Data Display Channel)
which is a serial channel through the VGA monitor connector
to a DDC-compliant monitor. This interface is defined by the
Video Electronics Standards Association (VESA). The standards
are available for purchase at http://www.vesa.org .
The second interface is a general-purpose I2C bus.
It may be connected to a TV-out chip such as the BT869 or possibly
to a digital flat-panel display.
Stability
---------
Apparently stable.
Supported Devices
-----------------
See above.
Features
--------
Both busses use the i2c-algo-bit driver for 'bit banging' and support
for specific transactions is provided by i2c-algo-bit.
Issues
------
If you enable bus testing in i2c-algo-bit (insmod i2c-algo-bit bit_test=1),
the test may fail; if so, the i2c-i810 driver won't be inserted.
However, we think this has been fixed.
-55
View File
@@ -1,55 +0,0 @@
Kernel driver `i2c-ipmb.o'
Status: IN DEVELOPMENT - DOESN'T WORK AT ALL - DON'T BOTHER TRYING IT
Supported adapters:
* IPMI BMC
Author: Mark D. Studebaker <mdsxyz123@yahoo.com>
Module Parameters
-----------------
(none)
Description
-----------
This is an adapter which provides access to the IPMB
(Intelligent Platform Management Bus) and other I2C / SMBus busses
behind the BMC (Baseboard Management Controller) via
IPMI (Intelligent Platform Management Interface).
This provides low-level access to all devices on busses
behind the BMC. For high-level access to sensors known
to the BMC, use the i2c-ipmi and bmcsensors modules.
This module REQUIRES the IPMI kernel patch by Corey Minyard,
v13 or higher, available at http://openipmi.sourceforge.net .
Other IPMI drivers are not supported.
See http://www.intel.com/design/servers/ipmi/ for
IPMI standards information.
You must configure the following features in your kernel
(under Character Devices):
IPMI top-level message handler
IPMI KCS handler
The IPMI device interface and IPMI watchdog handler are not required.
If the KCS handler (ipmi_kcs_drv) is configured as a module,
you must modprobe it separately, it will not load automatically
when you modprobe i2c-ipmi or ipmi_msghandler (unless you set up
/etc/modules.conf to load ipmi_kcs_drv when ipmi_msghandler is loaded).
Issues
------
Only one BMC is supported - shouldn't be a problem.
Alpha!
**********************
The lm_sensors project gratefully acknowledges the support of
Hewlett Packard (Ft. Collins) in the development of this driver.
-61
View File
@@ -1,61 +0,0 @@
Kernel driver `i2c-ipmi.o'
Status: Alpha
Supported adapters:
* IPMI BMC
Author: Mark D. Studebaker <mdsxyz123@yahoo.com>
Module Parameters
-----------------
(none)
Description
-----------
This is a "dummy" adapter which provides access to the
BMC (Baseboard Management Controller) via IPMI (Intelligent Platform
Management Interface). It is used by the bmcsensors module.
Together with the bmcsensors module, this provides high-level
access to sensors known by the BMC. For low-level access to
IPMB (I2C/SMBus) busses behind the BMC, use the i2c-ipmb module.
This module REQUIRES the IPMI kernel patch by Corey Minyard,
v13 or higher, available at http://openipmi.sourceforge.net .
Other IPMI drivers are not supported.
See http://www.intel.com/design/servers/ipmi/ for
IPMI standards information.
You must configure the following features in your kernel
(under Character Devices):
IPMI top-level message handler
IPMI KCS handler
The IPMI device interface and IPMI watchdog handler are not required.
If the KCS handler (ipmi_kcs_drv) is configured as a module,
you must modprobe it separately, it will not load automatically
when you modprobe i2c-ipmi or ipmi_msghandler (unless you set up
/etc/modules.conf to load ipmi_kcs_drv when ipmi_msghandler is loaded).
Issues
------
Only one BMC is supported - shouldn't be a problem.
Only one client is supported - to be fixed.
Libsensors was hacked to support this dummy driver; only
the ISA dummy driver was supported previously.
The library needs further enhancements for full
dummy driver support.
Alpha!
**********************
The lm_sensors project gratefully acknowledges the support of
Hewlett Packard (Ft. Collins) in the development of this driver.
-53
View File
@@ -1,53 +0,0 @@
Kernel driver i2c-nforce2
-------------------------
Status: Well tested
Supported adapters:
* nVidia nForce2 SMBus PCI interface
(10de:0064, included in nForce2 MCP,
10de:0084, included in nForce2 Ultra 400 MCP)
Datasheet: not publicly available, but seems to be similar to the
AMD-8111 SMBus 2.0 adapter.
* nVidia nForce3 SMBus PCI interface
(10de:00d4, included in nForce3 Pro150 MCP,
10de:00e4, included in nForce3 250Gb MCP)
Datasheet: same as for nForce2 SMBus PCI interface
* nVidia nForce4 SMBus PCI interface
(10de:0052, included in nForce4 MCP)
(10de:0034, included in nForce4 MCP-04)
(10de:0264, included in nForce4 MCP51)
(10de:0368, included in nForce4 MCP55)
Datasheet: same as for nForce2 SMBus PCI interface
Author: Hans-Frieder Vogt <hfvogt@arcor.de>
Description
-----------
i2c-nforce2 is a driver for the SMBuses included in the nVidia nForce2,
nForce3 and nForce4 MCP.
If your 'lspci -v' listing shows something like the following,
00:01.1 SMBus: nVidia Corporation: Unknown device 0064 (rev a2)
Subsystem: Asustek Computer, Inc.: Unknown device 0c11
Flags: 66Mhz, fast devsel, IRQ 5
I/O ports at c000 [size=32]
Capabilities: <available only to root>
then this driver should support the SMBuses of your motherboard.
Notes
-----
The SMBus adapter in the nForce2/3/4 chipset seems to be very similar to the
SMBus 2.0 adapter in the AMD-8111 southbridge. However, I could only get the
driver to work with direct I/O access, which is different to the EC interface
of the AMD-8111.
Tested on Asus A7N8X. The ACPI DSDT table of the Asus A7N8X lists two SMBuses,
both of which are supported by this driver.
-100
View File
@@ -1,100 +0,0 @@
Kernel driver `i2c-piix4.o'
Status: Complete and well-tested
except for Victory66 support which is Alpha.
Supported adapters:
* Intel 82371AB PIIX4 and PIIX4E
* Intel 82443MX (440MX)
Datasheet: Publicly available at the Intel website
* ServerWorks OSB4, CSB5, CSB6 and HT-1000 southbridges
Datasheet: Only available via NDA from ServerWorks
* ATI IXP200, IXP300, IXP400 and SB600 southbridges
Datasheet: Not publicly available
* Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge
Datasheet: Publicly available at the SMSC website http://www.smsc.com
Author: Frodo Looijaard <frodol@dds.nl> and Philip Edelbrock
<phil@netroedge.com>
Module Parameters
-----------------
* force: int
Forcibly enable the PIIX4. DANGEROUS!
* force_addr: int
Forcibly enable the PIIX4 at the given address. EXTREMELY DANGEROUS!
Description
-----------
The PIIX4 (properly known as the 82371AB) is an Intel chip with a lot of
functionality. Among other things, it implements the PCI bus. One of its
minor functions is implementing a System Management Bus. This is a true
SMBus - you can not access it on I2C levels. The good news is that it
natively understands SMBus commands and you do not have to worry about
timing problems. The bad news is that non-SMBus devices connected to it
can confuse it mightily. Yes, this is known to happen...
Cat /proc/pci, and see whether it contains an entry like this:
Bus 0, device 1, function 3:
Bridge: Intel 82371AB PIIX4 ACPI (rev 1).
Medium devsel. Fast back-to-back capable.
Bus and device numbers may differ, but the function number must be identical
(like many PCI devices, the PIIX4 incorporates a number of different
'functions', which can be considered as separate devices). If you find such
an entry, you have a PIIX4 SMBus controller.
On some computers (most notably, some Dells), the SMBus is disabled by
default. If you use the insmod parameter 'force=1', the kernel module
will try to enable it. THIS IS VERY DANGEROUS! If the BIOS did not
set up a correct address for this module, you could get in big trouble
(read: crashes, data corruption, etc.). Try this only as a last resort
(try BIOS updates first, for example), and backup first! An even more
dangerous option is 'force_addr=<IOPORT>'. This will not only enable the
PIIX4 like 'force' foes, but it will also set a new base I/O port address.
The SMBus parts of the PIIX4 needs a range of 8 of these addresses to
function correctly. If these addresses are already reserved by some other
device, you will get into big trouble! DON'T USE THIS IF YOU ARE NOT VERY
SURE ABOUT WHAT YOU ARE DOING!
The PIIX4E is just an new version of the PIIX4; it is supported as well. The
PIIX/PIIX3 does not implement an SMBus or I2C bus, so you can't use this
driver on those mainboards.
The ServerWorks Southbridges, the Intel 440MX,
and the Victory66 are identical to the PIIX4 in I2C/SMBus support.
If you own Force CPCI735 motherboard or other OSB4 based systems you may need
to change the SMBus Interrupt Select register so the SMBus controller uses
the SMI mode.
1) Use lspci command and locate the PCI device with the SMBus controller:
00:0f.0 ISA bridge: ServerWorks OSB4 South Bridge (rev 4f)
The line may vary for different chipsets. Please consult the driver source
for all possible PCI ids (and lspci -n to match them). Lets assume the
device is located at 00:0f.0.
2) Now you just need to change the value in 0xD2 register. Get it first with
command: lspci -xxx -s 00:0f.0
If the value is 0x3 then you need to change it to 0x1
setpci -s 00:0f.0 d2.b=1
Please note that you don't need to do that in all cases, just when the SMBus is
not working properly.
Hardware-specific issues
------------------------
This driver will refuse to load on IBM systems with an Intel PIIX4 SMBus.
Some of these machines have an RFID EEPROM (24RF08) connected to the SMBus,
which can easily get corrupted due to a state machine bug. These are mostly
Thinkpad laptops, but desktop systems may also be affected. We have no list
of all affected systems, so the only safe solution was to prevent access to
the SMBus on all IBM systems (detected using DMI data.)
For additional information, read README.thinkpad.
-42
View File
@@ -1,42 +0,0 @@
Kernel driver `i2c-savage4.o'
Status: Alpha
Supported adapters:
* Savage4
* Savage2000
Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
<phil@netroedge.com>, Ralph Metzler <rjkm@thp.uni-koeln.de>,
Alexander Wold, and Mark D. Studebaker <mdsxyz123@yahoo.com>
Main contact: Sensors mailing list
Module Parameters
-----------------
(none)
Description
-----------
The Savage4 chips contain two I2C interfaces (aka a I2C 'master'
or 'host').
The first interface is used for DDC (Data Display Channel)
which is a serial channel through the VGA monitor connector
to a DDC-compliant monitor. This interface is defined by the
Video Electronics Standards Association (VESA). The standards
are available for purchase at http://www.vesa.org .
The DDC bus is not yet supported because its register is not
directly memory-mapped.
The second interface is a general-purpose I2C bus.
This is the only interface supported by the driver at the moment.
This driver uses the i2c-algo-bit module which must be
modprobed and/or enabled in your kernel
(Character Devices -> I2C Support -> I2C Bit-Banging Interfaces).
-56
View File
@@ -1,56 +0,0 @@
Kernel driver `i2c-sis5595.o'
Status: Beta
Supported adapters:
* Silicon Integrated Systems Corp. SiS5595 Southbridge
Datasheet: Publicly available at the Silicon Integrated Systems Corp. site.
Author: Frodo Looijaard <frodol@dds.nl> and
Mark D. Studebaker <mdsxyz123@yahoo.com>
Note: all have mfr. ID 0x1039.
SUPPORTED PCI ID
5595 0008
Note: The chips below contain a 0008 device which is incompatible with the
5595. We recognize these by the presence of the listed
"blacklist" PCI ID and refuse to load.
NOT SUPPORTED PCI ID BLACKLIST PCI ID
540 0008 0540
550 0008 0550
5513 0008 5511
5581 0008 5597
5582 0008 5597
5597 0008 5597
5598 0008 5597/5598
630 0008 0630
645 0008 0645
730 0008 0730
735 0008 0735
Module Parameters
-----------------
force_addr=0xaddr Set the I/O base address. Useful for boards
that don't set the address in the BIOS. Does not do a
PCI force; the device must still be present in lspci.
Don't use this unless the driver complains that the
base address is not set.
Example: 'modprobe i2c-sis5595 force_addr=0x290'
Description
-----------
i2c-sis5595 is a true SMBus host driver for motherboards with the
SiS5595 southbridges.
WARNING: If you are trying to access the integrated sensors on the
SiS5595 chip, you want the sis5595.o driver for those, not this driver.
This driver is a BUS driver, not a CHIP driver.
A BUS driver is used by other CHIP drivers to access chips on the bus.
To install this driver, simply do
# modprobe i2c-sis5595
-63
View File
@@ -1,63 +0,0 @@
Kernel driver `i2c-sis630.o'
Status: beta
Supported adapters:
* Silicon Integrated Systems Corp (SiS)
630 chipset (Datasheet: available at http://amalysh.bei.t-online.de/docs/SIS/)
730 chipset
* Possible other SiS chipsets ?
Author: Alexander Malysh <amalysh@web.de>
License: GPL
Module Parameters
-----------------
force = [1|0] Forcibly enable the SIS630. DANGEROUS!
This can be interesting for chipsets not named
above to check if it works for you chipset, but DANGEROUS!
high_clock = [1|0] Forcibly set Host Master Clock to 56KHz (default,what your BIOS use).
DANGEROUS! This should be a bit faster, but freeze some systems (i.e.
my Laptop).
Description
-----------
This SMBus only driver is known to work on motherboards with the above named
chipsets.
If you see something like this:
00:00.0 Host bridge: Silicon Integrated Systems [SiS] 630 Host (rev 31)
00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513
or like this:
00:00.0 Host bridge: Silicon Integrated Systems [SiS] 730 Host (rev 02)
00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513
in your 'lspci' output , then this driver is for your chipset.
Note: kernel versions later than 2.4.18 not tested.
If anyone owns a board with those chips AND is willing to risk crashing &
burning an otherwise well-behaved kernel in the name of progress... please
contact me at <amalysh@web.de> or via the project's mailing list: see
http://www.lm-sensors.org/wiki/AuthorsAndContributors. Please send bug reports
and/or success stories as well.
Thank Yous
-----------
Philip Edelbrock <phil@netroedge.com>
- testing SiS730 support
Mark M. Hoffman <mhoffman@lightlink.com>
- bug fixes
To anyone else which I forgot here ;), thanks!
-85
View File
@@ -1,85 +0,0 @@
Kernel driver `i2c-sis645.o'
Status: Beta
Supported adapters:
* Silicon Integrated Systems Corp (SiS)
Any combination of these host bridges:
645, 645DX (aka 646), 648, 650, 651, 655, 735, 745, 746
and these south bridges:
961, 962, 963(L)
Author: Mark M. Hoffman <mhoffman@lightlink.com>
License: GPL
Module Parameters
-----------------
(none)
Description
-----------
This SMBus only driver is known to work on motherboards with the above named
chipset combinations. The driver was developed without benefit of a proper
datasheet from SiS. The SMBus registers are assumed compatible with those of
the SiS630, although they are located in a completely different place. Thanks
to Alexander Malysh <amalysh@web.de> for providing the SiS630 datasheet (and
driver).
The command "lspci" as root should produce something like these lines:
00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645
00:02.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513
00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016
or perhaps this...
00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645 (rev 02)
00:02.0 ISA bridge: Silicon Integrated Systems [SiS]: Unknown device 0961
00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016
(kernel versions later than 2.4.18 may fill in the "Unknown"s)
If the SMBus line is missing, then the BIOS on the board has disabled the
device. This driver is capable of enabling it, as long as the kernel has
CONFIG_HOTPLUG enabled. In that case, the SMBus device will appear in "lspci"
after the driver is loaded. Note that you need to rebuild lm_sensors AFTER
running "make config" or equivalent on your kernel, otherwise the i2c-sis645
driver will not build in this support.
I suspect that this driver could be made to work for the following SiS chipsets
as well: 635, and 635T.
If anyone owns a board with those chips AND is willing to risk crashing &
burning an otherwise well-behaved kernel in the name of progress... please
contact me at <mhoffman@lightlink.com> or via the project's mailing list:
see http://www.lm-sensors.org/wiki/AuthorsAndContributors. Please send bug reports
and/or success stories as well.
TO DOs
-----------
* The driver does not support SMBus block reads/writes; I may add them if a
scenario is found where they're needed.
Thank Yous
-----------
Mark D. Studebaker <mdsxyz123@yahoo.com>
- design hints and bug fixes
Alexander Maylsh <amalysh@web.de>
- ditto, plus an important datasheet... almost the one I really wanted
Hans-Günter Lütke Uphues <hg_lu@t-online.de>
- patch for SiS735
Robert Zwerus <arzie@dds.nl>
- testing for SiS645DX
Kianusch Sayah Karadji <kianusch@sk-tech.net>
- patch for SiS645DX/962
Ken Healy
- patch for Sis655
To anyone else who has written w/ feedback, thanks!
-46
View File
@@ -1,46 +0,0 @@
Kernel driver `i2c-tsunami.o'
Status: Stable
Supported adapters:
* DEC Tsunami 21272/Typhoon 21274 based boards
Author: Oleg I. Vdovikin <vdovikin@jscc.ru>
Main contact: Oleg I. Vdovikin <vdovikin@jscc.ru>
Module Parameters
-----------------
(none)
Description
-----------
The Tsunami/Typhoon chipsets' "Cchip" supports I2C busses via MPD registers
and appropriate open drain pins. This bus is usually used to read
SPD SDRAM DIMMs, however there may be other devices on
the bus also. These chips are used with wide range of 21264A boards.
This driver uses the i2c-algo-bit module which must be
modprobed and/or enabled in your kernel
(Character Devices -> I2C Support -> I2C Bit-Banging Interfaces).
Stability
---------
Apparently stable.
Supported Devices
-----------------
This driver was written and tested on the 'API UP2000(+)
boards'. As seems it should work with any Tsunami based system.
Features
--------
I2C busses use the i2c-algo-bit driver for 'bit banging' and support
for specific transactions is provided by i2c-algo-bit.
Issues
------
Not known.
-50
View File
@@ -1,50 +0,0 @@
Kernel driver `i2c-via.o'
Status: Complete and well-tested
Supported adapters:
* VIA Technologies, InC. VT82C586B
Datasheet: Publicly available at the VIA website
Author: Kyösti Mälkki <kmalkki@cc.hut.fi>
Module Parameters
-----------------
(none)
Description
-----------
i2c-via is an i2c bus driver for motherboards with VIA chipset.
The following VIA pci chipsets are supported:
- MVP3, VP3, VP2/97, VPX/97
- others with South bridge VT82C586B
Your /proc/pci or lspci listing must show this :
Bridge: VIA Technologies, Inc. VT82C586B ACPI (rev 10)
To install this driver, simply do
# modprobe i2c-via
This driver uses the i2c-algo-bit module which must be
modprobed and/or enabled in your kernel
(Character Devices -> I2C Support -> I2C Bit-Banging Interfaces).
Problems?
Q: You have VT82C586B on the motherboard, but not in the listing.
A: Go to your BIOS setup, section PCI devices or similar.
Turn USB support on, and try again.
Q: No error messages, but still i2c doesn't seem to work.
A: This can happen. This driver uses the pins VIA recommends in their
datasheets, but there are several ways the motherboard manufacturer
can actually wire the lines.
-61
View File
@@ -1,61 +0,0 @@
Kernel driver `i2c-viapro.o'
Status: Stable
Supported adapters:
* VIA Technologies, Inc. VT82C596A/B
Datasheet: Sometimes available at the VIA website
* VIA Technologies, Inc. VT82C686A/B
Datasheet: Sometimes available at the VIA website
* VIA Technologies, Inc. VT8231, VT8233, VT8233A, VT8235, VT8237R/A, VT8251
Datasheet: available on request and under NDA from VIA
* VIA Technologies, Inc. CX700
Datasheet: available on request and under NDA from VIA
Authors:
Kyösti Mälkki <kmalkki@cc.hut.fi>
Mark D. Studebaker <mdsxyz123@yahoo.com>
Jean Delvare <khali@linux-fr.org>
Module Parameters
-----------------
* force: int
Forcibly enable the SMBus controller. DANGEROUS!
* force_addr: int
Forcibly enable the SMBus at the given address. EXTREMELY DANGEROUS!
Description
-----------
i2c-viapro is a true SMBus host driver for motherboards with one of the
supported VIA south bridges.
Your /proc/pci or lspci -n listing must show one of these :
device 1106:3050 (VT82C596A function 3)
device 1106:3051 (VT82C596B function 3)
device 1106:3057 (VT82C686 function 4)
device 1106:3074 (VT8233)
device 1106:3147 (VT8233A)
device 1106:8235 (VT8231 function 4)
device 1106:3177 (VT8235)
device 1106:3227 (VT8237R)
device 1106:3337 (VT8237A)
device 1106:3287 (VT8251)
device 1106:8324 (CX700)
If none of these show up, you should look in the BIOS for settings like
enable ACPI / SMBus or even USB.
Except for the oldest chips (VT82C596A/B, VT82C686A and most probably
VT8231), this driver supports I2C block transactions. Such transactions
are mainly useful to read from and write to EEPROMs.
The CX700 additionally appears to support SMBus PEC, although this driver
doesn't implement it.
-102
View File
@@ -1,102 +0,0 @@
Virtual i2c bus
Brian Kuschak <bkuschak@xxxxxxxxx>
I2C devices must have a unique 7 bit address on the bus to which they
are attached. Many I2C chips have one or more input lines which can be
used to configure one or more of their address bits. This allows the
hardware designer to include multiple chips on a single bus by selecting
different addresses for each one. However, some chips have fixed addresses
which present a problem if more than one of them are required on a board.
Also, sometimes there are more devices than there are selectable addresses.
A common solution is to break the i2c bus into disjoint segments, by using
a multiplexor or i2c bus switch. This electrically isolates the individual
bus segments. Then the devices with duplicate addresses are placed on
separate bus segments, to prevent address conflicts.
This seems to be a very common approach in certain types of applications,
such as bladed chassis, where many identical 'blades' or cards are inserted
into a common backplane. The I2C bus on each blade is then located behind
a mux or i2c bus switch. This solution works great from the hardware
perspective, but presents some complexities for software.
The 'virtual i2c adapter' driver is an attempt to provide a flexible software
abstraction to simiplify these issues, while minimizing the impact on
existing code and fitting nicely into the i2c driver framework.
What is a 'virtual i2c adapter'
===============================
Think of this adapter as a layer above the existing algorithm and adapter
drivers. The virtual i2c adapter is the handle by which you access a
specific multiplexed bus segment. Each disjoint bus segment becomes a new
virtual bus. The virtual adapter is associated with a 'real' adapter and a
set of multiplexor control variables.
For example: you have an i2c-0 bus with one multiplexor or i2c bus switch,
and the mux has four outputs which can be electrically connected to the
i2c-0 bus. You would register four virtual i2c busses, one for each position
of the mux. As part of the registration, you provide callback functions to
select and deselect the mux as well as the selector values to be passed to
these functions.
--------- <--------> i2c-1 virtual
| i2c mux | <--------> i2c-2 virtual
i2c-0 <-------> | | <--------> i2c-3 virtual
ibm_ocp | 0x74 | <--------> i2c-4 virtual
---------
^
|
+--- (or externally controlled)
The mux may be i2c-controlled, in which case the mux_addr is set to the
slave address (0x74) and the mux_val would be the port number to select (0-3).
Alternatively, the mux could be controlled by some fixed memory-mapped register
in which case only the mux_val would be needed. The callback functions
are hardware specific and written by the user.
Once the virtual busses are registered, you may talk to any devices behind
the mux simply the using the i2c-1, i2c-2, i2c-3, etc. adapters instead of
the i2c-0 adapter. This makes the mux essentially transparent to the
i2c_client.
The great advantage of course is that all the existing sensor drivers and
other i2c client drivers automatically work, and the mux becomes transparent
to them.
Additional capabilities
=======================
This driver also supports multiple levels of muxing, by allowing the
user to register a virtual bus which has a different virtual bus as its
'real' adapter. In addition, each virtual bus may have different kinds of
multiplexors, with each bus having its own set of callback functions.
This allows you to easily support some very complex topologies.
In addition to multiplexing, some systems require that you obtain hardware
access to the bus behind the mux before talking on it. (Again, bladed chassis
typically 'fence' other blades off the bus to prevent a crashed blade from
bringing down the whole backplane.) Before talking on this bus, you have to
arbitrate with the other blades to acquire exclusive access to the bus. This
requirement is met by the addition of two new function pointers to the
algorithm drivers, acquire_exclusive() and release_exclusive(). Traditional
adapters will not use these and they can safely be left NULL. If they are
non-null, the virtual bus driver will call the acquire_exclusive() function
before attempting any operations on that virtual bus. This function may block,
and this is done before acquiring the lock on the underlying 'real' bus to
allow transactions on the real bus to continue unimpeded while waiting for
arbitrartion. In addition the ownership is only held for as long as it takes
to complete the transfer.
Files
=====
drivers/i2c/i2c-virtual.c: The 'algorithm' and 'adapter' driver
include/linux/i2c-virtual.h: Header file with the mux control structure
and callback prototypes.
-83
View File
@@ -1,83 +0,0 @@
Kernel driver `i2c-voodoo3.o'
Status: 'Beta' and still under development and testing
Supported adapters:
* 3dfx Voodoo3 based cards
* Voodoo Banshee based cards
Author: Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock
<phil@netroedge.com>, Ralph Metzler <rjkm@thp.uni-koeln.de>,
and Mark D. Studebaker <mdsxyz123@yahoo.com>
Main contact: Philip Edelbrock <phil@netroedge.com>
The code is based upon Ralph's test code (he did the hard stuff ;')
Module Parameters
-----------------
(none)
Description
-----------
The 3dfx Voodoo3 chip contains two I2C interfaces (aka a I2C 'master'
or 'host').
The first interface is used for DDC (Data Display Channel)
which is a serial channel through the VGA monitor connector
to a DDC-compliant monitor. This interface is defined by the
Video Electronics Standards Association (VESA). The standards
are available for purchase at http://www.vesa.org .
The second interface is a general-purpose I2C bus.
The intent by 3dfx was to allow manufacturers to add extra
chips to the video card such as a TV-out chip such as the BT869 or possibly
even I2C based temperature sensors like the ADM1021 or LM75.
The 'feature' set of this driver is based on the i2c-piix4 (SMBus)
features. I.e., SMBus byte reads and writes, as well as detected faults in
transmission. Quick reads/writes, block transactions, word transactions,
are yet unwritten but are probably not too hard to add.
This driver uses the i2c-algo-bit module which must be
modprobed and/or enabled in your kernel
(Character Devices -> I2C Support -> I2C Bit-Banging Interfaces).
Stability
---------
Seems to be stable on the test machine, but needs more testing on other
machines.
Simultaneous accesses of the DDC and I2C busses may cause errors.
Supported Devices
-----------------
Specifically, this driver was written and tested on the '3dfx Voodoo3 AGP
3000' which has a tv-out feature (s-video or composite). According to the
docs and discussions, this code should work for any Voodoo3 based cards as
well as Voodoo Banshee based cards. The DDC interface has been tested
on a Voodoo Banshee card.
Features
--------
This driver controls the I2C controller of the Voodoo3 chip. 'byte read'
transactions and 'data_byte write' transactions are supported, but all else
is not (but is probably easy to add if the need arises).
Issues
------
Probably many, but it seems to work OK on my system. :')
External Device Connection
--------------------------
The digital video input jumpers give availability to the I2C bus.
Specifically, pins 13 and 25 (bottom row middle, and bottom right-end) are
the I2C clock and I2C data lines, respectively. +5V and GND are probably
also easily available making the addition of extra I2C/SMBus devices easy
to implement.
+5 -6
View File
@@ -1,15 +1,14 @@
Installing Modules
==================
'make install' will install the kernel modules modules in the
subdirectories /lib/modules/$version/kernel/drivers/i2c and
/lib/modules/$version/kernel/drivers/sensors. This puts the modules
in the same location as a kernel 'make modules_install' does so
that there will be no duplicate modules.
'make install' will install the kernel modules in the
subdirectory /lib/modules/$version/kernel/drivers/i2c/chips.
This puts the modules in the same location as a kernel
'make modules_install' does so that there will be no duplicate modules.
If 'make install' fails, particularly on Mandrake kernels,
see FAQ #3.8 (fix MODPREF in the Makefile) and #3.9.
For details on options that apply to most modules, see
doc/chips/MODPARMS. For details on options specific to particular
modules, see the documentation in doc/chips/ and doc/busses/.
modules, see the documentation in doc/chips/.
-127
View File
@@ -1,127 +0,0 @@
# Module.mk - Makefile for a Linux module for reading sensor data.
# Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
#
# 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.
# Note that MODULE_DIR (the directory in which this file resides) is a
# 'simply expanded variable'. That means that its value is substituted
# verbatim in the rules, until it is redefined.
MODULE_DIR := kernel/busses
KERNELBUSSESDIR := $(MODULE_DIR)
# Regrettably, even 'simply expanded variables' will not put their currently
# defined value verbatim into the command-list of rules...
KERNELBUSSESTARGETS :=
ifeq ($(shell if grep -q '^CONFIG_IPMI_HANDLER=' $(LINUX)/.config; then echo 1; fi),1)
#doesn't work yet
#KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ipmb.o
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ipmi.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_ALI1535=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ali1535.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_ALI15X3=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ali15x3.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_ALI1563=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-ali1563.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_AMD756=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-amd756.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_AMD756_S4882=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-amd756-s4882.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_AMD8111=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-amd8111.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_HYDRA=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-hydra.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_I801=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-i801.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_I810=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-i810.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_ISA=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-isa.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_NFORCE2=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-nforce2.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_SIS5595=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-sis5595.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_SIS630=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-sis630.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_SIS645=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-sis645.o
endif
# don't compile dmi_scan unless x86 because it needs isa access
ifneq ($(shell if grep -q '^CONFIG_I2C_PIIX4=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-piix4.o
ifeq ($(shell if grep -q '^CONFIG_X86=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/dmi_scan.o
endif
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_SAVAGE4=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-savage4.o
endif
# don't compile unless alpha because of kernel include-file dependencies
ifeq ($(MACHINE),alpha)
ifneq ($(shell if grep -q '^CONFIG_I2C_TSUNAMI=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-tsunami.o
endif
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_VIA=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-via.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_VIAPRO=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-viapro.o
endif
ifneq ($(shell if grep -q '^CONFIG_I2C_VOODOO3=y' $(LINUX)/.config; then echo 1; fi),1)
KERNELBUSSESTARGETS += $(MODULE_DIR)/i2c-voodoo3.o
endif
# Include all dependency files
INCLUDEFILES += $(KERNELBUSSESTARGETS:.o=.d)
all-kernel-busses: $(KERNELBUSSESTARGETS)
all :: all-kernel-busses
#
# If $MODPREF/kernel exists, we presume the new (2.4.0) /lib/modules/x.y.z directory
# layout, so we install in kernel/drivers/i2c/busses and remove old versions in misc/
# and kernel/drivers/i2c/ . Otherwise we install in misc/ as before.
#
install-kernel-busses: all-kernel-busses
if [ -n "$(KERNELBUSSESTARGETS)" ] ; then \
$(MKDIR) $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/busses ; \
$(INSTALL) -m 644 $(KERNELBUSSESTARGETS) $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/busses ; \
for i in $(KERNELBUSSESTARGETS) ; do \
$(RM) $(DESTDIR)$(MODPREF)/misc/`basename $$i` $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/`basename $$i` \
$(DESTDIR)$(MODPREF)/kernel/drivers/i2c/`basename $$i`.gz $(DESTDIR)$(MODPREF)/kernel/drivers/i2c/busses/`basename $$i`.gz ; \
done ; \
$(RMDIR) $(DESTDIR)$(MODPREF)/misc 2> /dev/null || true ; \
fi
install :: install-kernel-busses
clean-kernel-busses:
$(RM) $(KERNELBUSSESDIR)/*.o $(KERNELBUSSESDIR)/*.d
clean :: clean-kernel-busses
-249
View File
@@ -1,249 +0,0 @@
/*
Taken from arch/i386/kernel/dmi_scan.c.
Changes dmi_ident to be non-static so we can access.
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/apm_bios.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <asm/io.h>
#include <asm/keyboard.h>
#include <asm/system.h>
#include "version.h"
int is_unsafe_smbus;
EXPORT_SYMBOL(is_unsafe_smbus);
struct dmi_header
{
u8 type;
u8 length;
u16 handle;
};
enum
{
DMI_BIOS_VENDOR,
DMI_BIOS_VERSION,
DMI_BIOS_DATE,
DMI_SYS_VENDOR,
DMI_PRODUCT_NAME,
DMI_PRODUCT_VERSION,
DMI_BOARD_VENDOR,
DMI_BOARD_NAME,
DMI_BOARD_VERSION,
DMI_STRING_MAX
};
#define dmi_printk(x)
//#define dmi_printk(x) printk x
static char * __init dmi_string(struct dmi_header *dm, u8 s)
{
u8 *bp=(u8 *)dm;
bp+=dm->length;
if(!s)
return "";
s--;
while(s>0 && *bp)
{
bp+=strlen(bp);
bp++;
s--;
}
return bp;
}
/*
* We have to be cautious here. We have seen BIOSes with DMI pointers
* pointing to completely the wrong place for example
*/
static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *))
{
u8 *buf;
struct dmi_header *dm;
u8 *data;
int i=0;
buf = ioremap(base, len);
if(buf==NULL)
return -1;
data = buf;
/*
* Stop when we see all the items the table claimed to have
* OR we run off the end of the table (also happens)
*/
while(i<num && data-buf+sizeof(struct dmi_header)<=len)
{
dm=(struct dmi_header *)data;
/*
* We want to know the total length (formated area and strings)
* before decoding to make sure we won't run off the table in
* dmi_decode or dmi_string
*/
data+=dm->length;
while(data-buf<len-1 && (data[0] || data[1]))
data++;
if(data-buf<len-1)
decode(dm);
data+=2;
i++;
}
iounmap(buf);
return 0;
}
inline static int __init dmi_checksum(u8 *buf)
{
u8 sum=0;
int a;
for(a=0; a<15; a++)
sum+=buf[a];
return (sum==0);
}
static int __init dmi_iterate(void (*decode)(struct dmi_header *))
{
u8 buf[15];
u32 fp=0xF0000;
#ifdef CONFIG_SIMNOW
/*
* Skip on x86/64 with simnow. Will eventually go away
* If you see this ifdef in 2.6pre mail me !
*/
return -1;
#endif
while( fp < 0xFFFFF)
{
isa_memcpy_fromio(buf, fp, 15);
if(memcmp(buf, "_DMI_", 5)==0 && dmi_checksum(buf))
{
u16 num=buf[13]<<8|buf[12];
u16 len=buf[7]<<8|buf[6];
u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8];
/*
* DMI version 0.0 means that the real version is taken from
* the SMBIOS version, which we don't know at this point.
*/
if(buf[14]!=0)
dmi_printk((KERN_INFO "DMI %d.%d present.\n",
buf[14]>>4, buf[14]&0x0F));
else
dmi_printk((KERN_INFO "DMI present.\n"));
dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n",
num, len));
dmi_printk((KERN_INFO "DMI table at 0x%08X.\n",
base));
if(dmi_table(base,len, num, decode)==0)
return 0;
}
fp+=16;
}
return -1;
}
char *dmi_ident[DMI_STRING_MAX];
/*
* Save a DMI string
*/
static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string)
{
char *d = (char*)dm;
char *p = dmi_string(dm, d[string]);
if(p==NULL || *p == 0)
return;
if (dmi_ident[slot])
return;
dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL);
if(dmi_ident[slot])
strcpy(dmi_ident[slot], p);
else
printk(KERN_ERR "dmi_save_ident: out of memory.\n");
}
/*
* Process a DMI table entry. Right now all we care about are the BIOS
* and machine entries. For 2.5 we should pull the smbus controller info
* out of here.
*/
static void __init dmi_decode(struct dmi_header *dm)
{
switch(dm->type)
{
case 0:
dmi_printk(("BIOS Vendor: %s\n",
dmi_string(dm, data[4])));
dmi_save_ident(dm, DMI_BIOS_VENDOR, 4);
dmi_printk(("BIOS Version: %s\n",
dmi_string(dm, data[5])));
dmi_save_ident(dm, DMI_BIOS_VERSION, 5);
dmi_printk(("BIOS Release: %s\n",
dmi_string(dm, data[8])));
dmi_save_ident(dm, DMI_BIOS_DATE, 8);
break;
case 1:
dmi_printk(("System Vendor: %s\n",
dmi_string(dm, data[4])));
dmi_save_ident(dm, DMI_SYS_VENDOR, 4);
dmi_printk(("Product Name: %s\n",
dmi_string(dm, data[5])));
dmi_save_ident(dm, DMI_PRODUCT_NAME, 5);
dmi_printk(("Version: %s\n",
dmi_string(dm, data[6])));
dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6);
dmi_printk(("Serial Number: %s\n",
dmi_string(dm, data[7])));
break;
case 2:
dmi_printk(("Board Vendor: %s\n",
dmi_string(dm, data[4])));
dmi_save_ident(dm, DMI_BOARD_VENDOR, 4);
dmi_printk(("Board Name: %s\n",
dmi_string(dm, data[5])));
dmi_save_ident(dm, DMI_BOARD_NAME, 5);
dmi_printk(("Board Version: %s\n",
dmi_string(dm, data[6])));
dmi_save_ident(dm, DMI_BOARD_VERSION, 6);
break;
}
}
int __init dmi_scan_mach(void)
{
int err;
printk("dmi_scan.o version %s (%s)\n", LM_VERSION, LM_DATE);
err = dmi_iterate(dmi_decode);
if(err)
printk("dmi_scan.o: SM BIOS not found\n");
else
printk("dmi_scan.o: SM BIOS found\n");
if(dmi_ident[DMI_SYS_VENDOR] != NULL
&& strncmp(dmi_ident[DMI_SYS_VENDOR], "IBM", 3) == 0)
is_unsafe_smbus = 1;
return 0;
}
MODULE_DESCRIPTION("SM BIOS DMI Scanner");
MODULE_LICENSE("GPL");
module_init(dmi_scan_mach);
-529
View File
@@ -1,529 +0,0 @@
/*
i2c-ali1535.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2000 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
Mark D. Studebaker <mdsxyz123@yahoo.com>,
Dan Eaton <dan.eaton@rocketlogix.com> and
Stephen Rousset<stephen.rousset@rocketlogix.com>
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.
*/
/*
This is the driver for the SMB Host controller on
Acer Labs Inc. (ALI) M1535 South Bridge.
The M1535 is a South bridge for portable systems.
It is very similar to the M15x3 South bridges also produced
by Acer Labs Inc. Some of the registers within the part
have moved and some have been redefined slightly. Additionally,
the sequencing of the SMBus transactions has been modified
to be more consistent with the sequencing recommended by
the manufacturer and observed through testing. These
changes are reflected in this driver and can be identified
by comparing this driver to the i2c-ali15x3 driver.
For an overview of these chips see http://www.acerlabs.com
The SMB controller is part of the 7101 device, which is an
ACPI-compliant Power Management Unit (PMU).
The whole 7101 device has to be enabled for the SMB to work.
You can't just enable the SMB alone.
The SMB and the ACPI have separate I/O spaces.
We make sure that the SMB is enabled. We leave the ACPI alone.
This driver controls the SMB Host only.
This driver does not use interrupts.
*/
/* Note: we assume there can only be one ALI1535, with one SMBus interface */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
/* ALI1535 SMBus address offsets */
#define SMBHSTSTS (0 + ali1535_smba)
#define SMBHSTTYP (1 + ali1535_smba)
#define SMBHSTPORT (2 + ali1535_smba)
#define SMBHSTCMD (7 + ali1535_smba)
#define SMBHSTADD (3 + ali1535_smba)
#define SMBHSTDAT0 (4 + ali1535_smba)
#define SMBHSTDAT1 (5 + ali1535_smba)
#define SMBBLKDAT (6 + ali1535_smba)
/* PCI Address Constants */
#define SMBCOM 0x004
#define SMBREV 0x008
#define SMBCFG 0x0D1
#define SMBBA 0x0E2
#define SMBHSTCFG 0x0F0
#define SMBCLK 0x0F2
/* Other settings */
#define MAX_TIMEOUT 500 /* times 1/100 sec */
#define ALI1535_SMB_IOSIZE 32
/*
*/
#define ALI1535_SMB_DEFAULTBASE 0x8040
/* ALI1535 address lock bits */
#define ALI1535_LOCK 0x06 < dwe >
/* ALI1535 command constants */
#define ALI1535_QUICK 0x00
#define ALI1535_BYTE 0x10
#define ALI1535_BYTE_DATA 0x20
#define ALI1535_WORD_DATA 0x30
#define ALI1535_BLOCK_DATA 0x40
#define ALI1535_I2C_READ 0x60
#define ALI1535_DEV10B_EN 0x80 /* Enable 10-bit addressing in */
/* I2C read */
#define ALI1535_T_OUT 0x08 /* Time-out Command (write) */
#define ALI1535_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */
/* Alert-Response-Address */
/* (read) */
#define ALI1535_KILL 0x04 /* Kill Command (write) */
#define ALI1535_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */
/* Alert-Response-Address */
/* (read) */
#define ALI1535_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */
/* of 10-bit address in I2C */
/* Read Command */
/* ALI1535 status register bits */
#define ALI1535_STS_IDLE 0x04
#define ALI1535_STS_BUSY 0x08 /* host busy */
#define ALI1535_STS_DONE 0x10 /* transaction complete */
#define ALI1535_STS_DEV 0x20 /* device error */
#define ALI1535_STS_BUSERR 0x40 /* bus error */
#define ALI1535_STS_FAIL 0x80 /* failed bus transaction */
#define ALI1535_STS_ERR 0xE0 /* all the bad error bits */
#define ALI1535_BLOCK_CLR 0x04 /* reset block data index */
/* ALI1535 device address register bits */
#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */
/* Address field */
/* -> Write = 0 */
/* -> Read = 1 */
#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */
static struct pci_driver ali1535_driver;
static unsigned short ali1535_smba = 0;
/* Detect whether a ALI1535 can be found, and initialize it, where necessary.
Note the differences between kernels with the old PCI BIOS interface and
newer kernels with the real PCI interface. In compat.h some things are
defined to make the transition easier. */
int ali1535_setup(struct pci_dev *ALI1535_dev)
{
int error_return = 0;
unsigned char temp;
/* Check the following things:
- SMB I/O address is initialized
- Device is enabled
- We can use the addresses
*/
/* Determine the address of the SMBus area */
pci_read_config_word(ALI1535_dev, SMBBA, &ali1535_smba);
ali1535_smba &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1));
if (ali1535_smba == 0) {
printk
("i2c-ali1535.o: ALI1535_smb region uninitialized - upgrade BIOS?\n");
error_return = -ENODEV;
}
if (error_return == -ENODEV)
goto END;
if (check_region(ali1535_smba, ALI1535_SMB_IOSIZE)) {
printk
("i2c-ali1535.o: ALI1535_smb region 0x%x already in use!\n",
ali1535_smba);
error_return = -ENODEV;
}
if (error_return == -ENODEV)
goto END;
/* check if whole device is enabled */
pci_read_config_byte(ALI1535_dev, SMBCFG, &temp);
if ((temp & ALI1535_SMBIO_EN) == 0) {
printk
("i2c-ali1535.o: SMB device not enabled - upgrade BIOS?\n");
error_return = -ENODEV;
goto END;
}
/* Is SMB Host controller enabled? */
pci_read_config_byte(ALI1535_dev, SMBHSTCFG, &temp);
if ((temp & 1) == 0) {
printk
("i2c-ali1535.o: SMBus controller not enabled - upgrade BIOS?\n");
error_return = -ENODEV;
goto END;
}
/* set SMB clock to 74KHz as recommended in data sheet */
pci_write_config_byte(ALI1535_dev, SMBCLK, 0x20);
/* Everything is happy, let's grab the memory and set things up. */
request_region(ali1535_smba, ALI1535_SMB_IOSIZE, ali1535_driver.name);
#ifdef DEBUG
/*
The interrupt routing for SMB is set up in register 0x77 in the
1533 ISA Bridge device, NOT in the 7101 device.
Don't bother with finding the 1533 device and reading the register.
if ((....... & 0x0F) == 1)
printk("i2c-ali1535.o: ALI1535 using Interrupt 9 for SMBus.\n");
*/
pci_read_config_byte(ALI1535_dev, SMBREV, &temp);
printk("i2c-ali1535.o: SMBREV = 0x%X\n", temp);
printk("i2c-ali1535.o: ALI1535_smba = 0x%X\n", ali1535_smba);
#endif /* DEBUG */
END:
return error_return;
}
#define MAX_TIMEOUT_HEROS 100
#define MAX_TIMEOUT_BLOCK_HEROS 500
#define MAX_TRY_HEROS 3
/* believe it or not this is the delay technique recommended by ALI */
static void ali1535_delay_loop(void)
{
int i;
for(i=0;i<30;i++)
outb_p(0,0xEB);
}
static int ali1535_wait_for_status(int count,int status)
{
int i;
int dat;
for (i = 0; i < count; i++) {
ali1535_delay_loop();
dat = inb_p(SMBHSTSTS);
if (dat == status)
break;
}
return i == count ? 1 : 0;
}
/* Return -1 on error. */
s32 ali1535_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data * data)
{
int i, len;
s32 result = 0;
int timeout = 0;
int oldsize = size;
repeat:
if(timeout++ > MAX_TRY_HEROS) {
result = -1;
goto EXIT;
}
/* clear status */
outb_p(0xFF, SMBHSTSTS);
if (ali1535_wait_for_status(MAX_TIMEOUT_HEROS,ALI1535_STS_IDLE))
goto repeat;
outb_p(ALI1535_KILL,SMBHSTTYP);
if (ali1535_wait_for_status(MAX_TIMEOUT_HEROS,ALI1535_STS_FAIL))
goto repeat;
/* clear status */
outb_p(0xFF, SMBHSTSTS);
if (ali1535_wait_for_status(MAX_TIMEOUT_HEROS,ALI1535_STS_IDLE))
goto repeat;
switch (size) {
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
ali1535_delay_loop();
size = ALI1535_QUICK;
outb_p(size, SMBHSTTYP); /* output command */
ali1535_delay_loop();
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
ali1535_delay_loop();
size = ALI1535_BYTE;
outb_p(size, SMBHSTTYP); /* output command */
ali1535_delay_loop();
if (read_write == I2C_SMBUS_WRITE) {
outb_p(command, SMBHSTCMD);
ali1535_delay_loop();
}
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
ali1535_delay_loop();
size = ALI1535_BYTE_DATA;
outb_p(size, SMBHSTTYP); /* output command */
ali1535_delay_loop();
outb_p(command, SMBHSTCMD);
ali1535_delay_loop();
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->byte, SMBHSTDAT0);
ali1535_delay_loop();
}
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
ali1535_delay_loop();
size = ALI1535_WORD_DATA;
outb_p(size, SMBHSTTYP); /* output command */
outb_p(command, SMBHSTCMD);
ali1535_delay_loop();
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
ali1535_delay_loop();
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
ali1535_delay_loop();
}
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD);
ali1535_delay_loop();
size = ALI1535_BLOCK_DATA;
outb_p(size, SMBHSTTYP); /* output command */
outb_p(command, SMBHSTCMD);
ali1535_delay_loop();
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0) {
len = 0;
data->block[0] = len;
}
if (len > 32) {
len = 32;
data->block[0] = len;
}
outb_p(len, SMBHSTDAT0);
ali1535_delay_loop();
{
int val;
val = inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR;
ali1535_delay_loop();
outb_p(val, SMBHSTTYP); /* Reset SMBBLKDAT */
ali1535_delay_loop();
}
for (i = 1; i <= len; i++) {
outb_p(data->block[i], SMBBLKDAT);
ali1535_delay_loop();
}
}
break;
default:
printk
(KERN_WARNING "i2c-ali1535.o: Unsupported transaction %d\n", size);
result = -1;
goto EXIT;
}
/* start the transaction */
outb_p(0xFF, SMBHSTPORT);
ali1535_delay_loop();
if (ali1535_wait_for_status(size == ALI1535_BLOCK_DATA ?
MAX_TIMEOUT_BLOCK_HEROS :
MAX_TIMEOUT_HEROS ,
ALI1535_STS_IDLE | ALI1535_STS_DONE)) {
size = oldsize;
goto repeat;
}
/* clear status */
outb_p(0xFF, SMBHSTSTS);
ali1535_delay_loop();
if(inb_p(SMBHSTSTS) != ALI1535_STS_IDLE) {
size = oldsize;
goto repeat;
}
if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) {
result = 0;
goto EXIT;
}
switch (size) {
case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */
data->byte = inb_p(SMBHSTDAT0);
ali1535_delay_loop();
break;
case ALI1535_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
ali1535_delay_loop();
break;
case ALI1535_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
ali1535_delay_loop();
break;
case ALI1535_BLOCK_DATA:
len = inb_p(SMBHSTDAT0);
ali1535_delay_loop();
if (len > 32)
len = 32;
data->block[0] = len;
{
int val;
val = inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR;
ali1535_delay_loop();
outb_p(val, SMBHSTTYP); /* Reset SMBBLKDAT */
ali1535_delay_loop();
}
for (i = 1; i <= data->block[0]; i++) {
data->block[i] = inb_p(SMBBLKDAT);
ali1535_delay_loop();
#ifdef DEBUG
printk
("i2c-ali1535.o: Blk: len=%d, i=%d, data=%02x\n",
len, i, data->block[i]);
#endif /* DEBUG */
}
break;
}
EXIT:
return result;
}
static void ali1535_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void ali1535_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
u32 ali1535_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-i2c SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = ali1535_access,
.functionality = ali1535_func,
};
static struct i2c_adapter ali1535_adapter = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535,
.algo = &smbus_algorithm,
.inc_use = ali1535_inc,
.dec_use = ali1535_dec,
};
static struct pci_device_id ali1535_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_AL,
.device = PCI_DEVICE_ID_AL_M7101,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (ali1535_setup(dev)) {
printk
("i2c-ali1535.o: ALI1535 not detected, module not inserted.\n");
return -ENODEV;
}
sprintf(ali1535_adapter.name, "SMBus ALI1535 adapter at %04x",
ali1535_smba);
return i2c_add_adapter(&ali1535_adapter);
}
static void __devexit ali1535_remove(struct pci_dev *dev)
{
i2c_del_adapter(&ali1535_adapter);
release_region(ali1535_smba, ALI1535_SMB_IOSIZE);
}
static struct pci_driver ali1535_driver = {
.name = "ali1535 smbus",
.id_table = ali1535_ids,
.probe = ali1535_probe,
.remove = __devexit_p(ali1535_remove),
};
static int __init i2c_ali1535_init(void)
{
printk("i2c-ali1535.o version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&ali1535_driver);
}
static void __exit i2c_ali1535_exit(void)
{
pci_unregister_driver(&ali1535_driver);
}
MODULE_AUTHOR
("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, "
"Mark D. Studebaker <mdsxyz123@yahoo.com> and Dan Eaton <dan.eaton@rocketlogix.com>");
MODULE_DESCRIPTION("ALI1535 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_ali1535_init);
module_exit(i2c_ali1535_exit);
-464
View File
@@ -1,464 +0,0 @@
/**
* i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge
*
* Copyright (C) 2004, 2005 Patrick Mochel, Chunhao Huang
*
* The 1563 southbridge is deceptively similar to the 1533, with a
* few notable exceptions. One of those happens to be the fact they
* upgraded the i2c core to be 2.0 compliant, and happens to be almost
* identical to the i2c controller found in the Intel 801 south
* bridges.
*
* This driver is based on a mix of the 15x3, 1535, and i801 drivers,
* with a little help from the ALi 1563 spec.
*
* Chunhao Huang / Winbond
* Backport to linux-2.4 from linux-2.6 on Mar.2005
*
* This file is released under the GPLv2
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/init.h>
#include "version.h"
#include "sensors_compat.h"
#ifndef PCI_DEVICE_ID_AL_M1563
#define PCI_DEVICE_ID_AL_M1563 0x1563
#endif
#define ALI1563_MAX_TIMEOUT 500
#define ALI1563_SMBBA 0x80
#define ALI1563_SMB_IOEN 1
#define ALI1563_SMB_HOSTEN 2
#define ALI1563_SMB_IOSIZE 16
#define SMB_HST_STS (ali1563_smba + 0)
#define SMB_HST_CNTL1 (ali1563_smba + 1)
#define SMB_HST_CNTL2 (ali1563_smba + 2)
#define SMB_HST_CMD (ali1563_smba + 3)
#define SMB_HST_ADD (ali1563_smba + 4)
#define SMB_HST_DAT0 (ali1563_smba + 5)
#define SMB_HST_DAT1 (ali1563_smba + 6)
#define SMB_BLK_DAT (ali1563_smba + 7)
#define HST_STS_BUSY 0x01
#define HST_STS_INTR 0x02
#define HST_STS_DEVERR 0x04
#define HST_STS_BUSERR 0x08
#define HST_STS_FAIL 0x10
#define HST_STS_DONE 0x80
#define HST_STS_BAD 0x1c
#define HST_CNTL1_TIMEOUT 0x80
#define HST_CNTL1_LAST 0x40
#define HST_CNTL2_KILL 0x04
#define HST_CNTL2_START 0x40
#define HST_CNTL2_QUICK 0x00
#define HST_CNTL2_BYTE 0x01
#define HST_CNTL2_BYTE_DATA 0x02
#define HST_CNTL2_WORD_DATA 0x03
#define HST_CNTL2_BLOCK 0x05
#define HST_CNTL2_SIZEMASK 0x38
static struct pci_driver ali1563_pci_driver;
static unsigned short ali1563_smba;
static int ali1563_transaction(struct i2c_adapter * a)
{
u32 data;
int timeout;
printk(KERN_DEBUG "ali1563: Transaction (pre): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
inb_p(SMB_HST_DAT1));
data = inb_p(SMB_HST_STS);
if (data & HST_STS_BAD) {
printk(KERN_WARNING "ali1563: Trying to reset busy device\n");
outb_p(data | HST_STS_BAD,SMB_HST_STS);
data = inb_p(SMB_HST_STS);
if (data & HST_STS_BAD){
return -EBUSY;
}
}
outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);
timeout = ALI1563_MAX_TIMEOUT;
do
i2c_delay(1);
while (((data = inb_p(SMB_HST_STS)) & HST_STS_BUSY) && --timeout);
printk(KERN_DEBUG "ali1563: Transaction (post): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
inb_p(SMB_HST_DAT1));
if (timeout && !(data & HST_STS_BAD)) {
return 0;
}
/* modified by Rudolf */
if (!timeout) {
printk(KERN_ERR "ali1563: Timeout - Trying to KILL transaction!\n");
/* Issue 'kill' to host controller */
outb_p(HST_CNTL2_KILL,SMB_HST_CNTL2);
data = inb_p(SMB_HST_STS);
}
/* device error - probably missing ACK */
if (data & HST_STS_DEVERR) {
printk(KERN_DEBUG "ali1563: Device error!\n");
}
/* bus collision */
if (data & HST_STS_BUSERR) {
printk(KERN_ERR "ali1563: Bus collision!\n");
/* Issue timeout, hoping it helps */
outb_p(HST_CNTL1_TIMEOUT,SMB_HST_CNTL1);
}
if (data & HST_STS_FAIL) {
printk(KERN_ERR "ali1563: Cleaning fail after KILL!\n");
outb_p(0x0,SMB_HST_CNTL2);
}
return -1;
}
static int ali1563_block_start(struct i2c_adapter * a)
{
u32 data;
int timeout;
printk(KERN_DEBUG "ali1563: Block (pre): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
inb_p(SMB_HST_DAT1));
data = inb_p(SMB_HST_STS);
if (data & HST_STS_BAD) {
printk(KERN_WARNING "ali1563: Trying to reset busy device\n");
outb_p(data | HST_STS_BAD,SMB_HST_STS);
data = inb_p(SMB_HST_STS);
if (data & HST_STS_BAD){
return -EBUSY;
}
}
/* Clear byte-ready bit */
outb_p(data | HST_STS_DONE, SMB_HST_STS);
/* Start transaction and wait for byte-ready bit to be set */
outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_START, SMB_HST_CNTL2);
timeout = ALI1563_MAX_TIMEOUT;
do
i2c_delay(1);
while (!((data = inb_p(SMB_HST_STS)) & HST_STS_DONE) && --timeout);
printk(KERN_DEBUG "ali1563: Block (post): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
inb_p(SMB_HST_STS), inb_p(SMB_HST_CNTL1), inb_p(SMB_HST_CNTL2),
inb_p(SMB_HST_CMD), inb_p(SMB_HST_ADD), inb_p(SMB_HST_DAT0),
inb_p(SMB_HST_DAT1));
if (timeout && !(data & HST_STS_BAD)){
return 0;
}
printk(KERN_WARNING "ali1563: SMBus Error: %s%s%s%s%s\n",
timeout ? "Timeout " : "",
data & HST_STS_FAIL ? "Transaction Failed " : "",
data & HST_STS_BUSERR ? "No response or Bus Collision " : "",
data & HST_STS_DEVERR ? "Device Error " : "",
!(data & HST_STS_DONE) ? "Transaction Never Finished " : "");
return -1;
}
static int ali1563_block(struct i2c_adapter * a, union i2c_smbus_data * data, u8 rw)
{
int i, len;
int error = 0;
/* Do we need this? */
outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1);
if (rw == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 1)
len = 1;
else if (len > 32)
len = 32;
outb_p(len,SMB_HST_DAT0);
outb_p(data->block[1],SMB_BLK_DAT);
} else
len = 32;
outb_p(inb_p(SMB_HST_CNTL2) | HST_CNTL2_BLOCK, SMB_HST_CNTL2);
for (i = 0; i < len; i++) {
if (rw == I2C_SMBUS_WRITE) {
outb_p(data->block[i + 1], SMB_BLK_DAT);
if ((error = ali1563_block_start(a)))
break;
} else {
if ((error = ali1563_block_start(a)))
break;
if (i == 0) {
len = inb_p(SMB_HST_DAT0);
if (len < 1)
len = 1;
else if (len > 32)
len = 32;
}
data->block[i+1] = inb_p(SMB_BLK_DAT);
}
}
/* Do we need this? */
outb_p(HST_CNTL1_LAST,SMB_HST_CNTL1);
return error;
}
static s32 ali1563_access(struct i2c_adapter * a, u16 addr,
unsigned short flags, char rw, u8 cmd,
int size, union i2c_smbus_data * data)
{
int error = 0;
int timeout;
u32 reg;
for (timeout = ALI1563_MAX_TIMEOUT; timeout; timeout--) {
if (!(reg = inb_p(SMB_HST_STS) & HST_STS_BUSY))
break;
}
if (!timeout)
printk(KERN_WARNING "ali1563: SMBus not idle. HST_STS = %02x\n",reg);
outb_p(0xff,SMB_HST_STS);
/* Map the size to what the chip understands */
switch (size) {
case I2C_SMBUS_PROC_CALL:
printk(KERN_ERR "ali1563: I2C_SMBUS_PROC_CALL not supported!\n");
error = -EINVAL;
break;
case I2C_SMBUS_QUICK:
size = HST_CNTL2_QUICK;
break;
case I2C_SMBUS_BYTE:
size = HST_CNTL2_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
size = HST_CNTL2_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
size = HST_CNTL2_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
size = HST_CNTL2_BLOCK;
break;
}
outb_p(((addr & 0x7f) << 1) | (rw & 0x01), SMB_HST_ADD);
outb_p( (inb_p(SMB_HST_CNTL2)&~HST_CNTL2_SIZEMASK) | (size << 3), SMB_HST_CNTL2);
/* Write the command register */
switch(size) {
case HST_CNTL2_BYTE:
if (rw== I2C_SMBUS_WRITE)
/* outb_p(cmd, SMB_HST_CMD); */
outb_p(cmd, SMB_HST_DAT0); /* modify by Rudolf */
break;
case HST_CNTL2_BYTE_DATA:
outb_p(cmd, SMB_HST_CMD);
if (rw == I2C_SMBUS_WRITE)
outb_p(data->byte, SMB_HST_DAT0);
break;
case HST_CNTL2_WORD_DATA:
outb_p(cmd, SMB_HST_CMD);
if (rw == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMB_HST_DAT0);
outb_p((data->word & 0xff00) >> 8, SMB_HST_DAT1);
}
break;
case HST_CNTL2_BLOCK:
outb_p(cmd, SMB_HST_CMD);
error = ali1563_block(a,data,rw);
goto Done;
}
if ((error = ali1563_transaction(a))){
goto Done;
}
if ((rw == I2C_SMBUS_WRITE) || (size == HST_CNTL2_QUICK)){
goto Done;
}
switch (size) {
case HST_CNTL2_BYTE: /* Result put in SMBHSTDAT0 */
data->byte = inb_p(SMB_HST_DAT0);
break;
case HST_CNTL2_BYTE_DATA:
data->byte = inb_p(SMB_HST_DAT0);
break;
case HST_CNTL2_WORD_DATA:
data->word = inb_p(SMB_HST_DAT0) + (inb_p(SMB_HST_DAT1) << 8);
break;
}
Done:
return error;
}
static u32 ali1563_func(struct i2c_adapter * a)
{
return ( I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA );
}
static int __devinit ali1563_setup(struct pci_dev * dev)
{
u16 ctrl;
pci_read_config_word(dev,ALI1563_SMBBA,&ctrl);
/* SMB I/O Base in high 12 bits and must be aligned with the
* size of the I/O space. */
ali1563_smba = ctrl & ~(ALI1563_SMB_IOSIZE - 1);
if (!ali1563_smba) {
printk(KERN_WARNING "ali1563: ali1563_smba Uninitialized\n");
goto Err;
}
/* Check if device is enabled */
if (!(ctrl & ALI1563_SMB_HOSTEN)) {
printk(KERN_WARNING "ali1563: Host Controller not enabled\n");
goto Err;
}
if (!(ctrl & ALI1563_SMB_IOEN)) {
printk(KERN_WARNING "I/O space not enabled, trying manually\n");
pci_write_config_word(dev, ALI1563_SMBBA,
ctrl | ALI1563_SMB_IOEN);
pci_read_config_word(dev, ALI1563_SMBBA, &ctrl);
if (!(ctrl & ALI1563_SMB_IOEN)) {
printk(KERN_WARNING "I/O space still not enabled, "
"giving up\n");
goto Err;
}
}
if (!request_region(ali1563_smba, ALI1563_SMB_IOSIZE,
ali1563_pci_driver.name)) {
printk(KERN_WARNING "ali1563: Could not allocate I/O space "
"at 0x%04x\n", ali1563_smba);
goto Err;
}
printk(KERN_INFO "ali1563: Found ALi1563 SMBus at 0x%04x\n",
ali1563_smba);
return 0;
Err:
return -ENODEV;
}
static void ali1563_shutdown(struct pci_dev *dev)
{
release_region(ali1563_smba,ALI1563_SMB_IOSIZE);
}
static void ali1563_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void ali1563_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static struct i2c_algorithm ali1563_algorithm = {
.name = "Non-i2c SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = ali1563_access,
.functionality = ali1563_func,
};
static struct i2c_adapter ali1563_adapter = {
.algo = &ali1563_algorithm,
.inc_use = ali1563_inc,
.dec_use = ali1563_dec,
};
static int __devinit ali1563_probe(struct pci_dev * dev,
const struct pci_device_id * id_table)
{
int error;
if ((error = ali1563_setup(dev))) {
printk(KERN_WARNING "ali1563: ALi1563 SMBus probe failed "
"(%d)\n", error);
return error;
}
sprintf(ali1563_adapter.name,"SMBus ALi 1563 Adapter @ %04x",
ali1563_smba);
if ((error = i2c_add_adapter(&ali1563_adapter)))
ali1563_shutdown(dev);
return error;
}
static void __devexit ali1563_remove(struct pci_dev * dev)
{
i2c_del_adapter(&ali1563_adapter);
ali1563_shutdown(dev);
}
static struct pci_device_id ali1563_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_AL,
.device = PCI_DEVICE_ID_AL_M1563,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static struct pci_driver ali1563_pci_driver = {
.name = "ali1563 smbus",
.id_table = ali1563_ids,
.probe = ali1563_probe,
.remove = __devexit_p(ali1563_remove),
};
static int __init ali1563_init(void)
{
printk("i2c-ali1563.o version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&ali1563_pci_driver);
}
module_init(ali1563_init);
static void __exit ali1563_exit(void)
{
pci_unregister_driver(&ali1563_pci_driver);
}
module_exit(ali1563_exit);
MODULE_AUTHOR("Patrick Mochel <mochel@digitalimplant.org>, Chunhao Huang");
MODULE_DESCRIPTION("ALi M1563 southbridge driver for linux-2.4, backported by Chunhao Huang from linux-2.6");
MODULE_LICENSE("GPL");
-548
View File
@@ -1,548 +0,0 @@
/*
ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com> and
Mark D. Studebaker <mdsxyz123@yahoo.com>
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.
*/
/*
This is the driver for the SMB Host controller on
Acer Labs Inc. (ALI) M1541 and M1543C South Bridges.
The M1543C is a South bridge for desktop systems.
The M1533 is a South bridge for portable systems.
They are part of the following ALI chipsets:
"Aladdin Pro 2": Includes the M1621 Slot 1 North bridge
with AGP and 100MHz CPU Front Side bus
"Aladdin V": Includes the M1541 Socket 7 North bridge
with AGP and 100MHz CPU Front Side bus
"Aladdin IV": Includes the M1541 Socket 7 North bridge
with host bus up to 83.3 MHz.
For an overview of these chips see http://www.acerlabs.com
The M1533/M1543C devices appear as FOUR separate devices
on the PCI bus. An output of lspci will show something similar
to the following:
00:02.0 USB Controller: Acer Laboratories Inc. M5237
00:03.0 Bridge: Acer Laboratories Inc. M7101
00:07.0 ISA bridge: Acer Laboratories Inc. M1533
00:0f.0 IDE interface: Acer Laboratories Inc. M5229
The SMB controller is part of the 7101 device, which is an
ACPI-compliant Power Management Unit (PMU).
The whole 7101 device has to be enabled for the SMB to work.
You can't just enable the SMB alone.
The SMB and the ACPI have separate I/O spaces.
We make sure that the SMB is enabled. We leave the ACPI alone.
This driver controls the SMB Host only.
The SMB Slave controller on the M15X3 is not enabled.
This driver does not use interrupts.
*/
/* Note: we assume there can only be one ALI15X3, with one SMBus interface */
/* #define DEBUG 1 */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
/* ALI15X3 SMBus address offsets */
#define SMBHSTSTS (0 + ali15x3_smba)
#define SMBHSTCNT (1 + ali15x3_smba)
#define SMBHSTSTART (2 + ali15x3_smba)
#define SMBHSTCMD (7 + ali15x3_smba)
#define SMBHSTADD (3 + ali15x3_smba)
#define SMBHSTDAT0 (4 + ali15x3_smba)
#define SMBHSTDAT1 (5 + ali15x3_smba)
#define SMBBLKDAT (6 + ali15x3_smba)
/* PCI Address Constants */
#define SMBCOM 0x004
#define SMBBA 0x014
#define SMBATPC 0x05B /* used to unlock xxxBA registers */
#define SMBHSTCFG 0x0E0
#define SMBSLVC 0x0E1
#define SMBCLK 0x0E2
#define SMBREV 0x008
/* Other settings */
#define MAX_TIMEOUT 200 /* times 1/100 sec */
#define ALI15X3_SMB_IOSIZE 32
/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB.
We don't use these here. If the bases aren't set to some value we
tell user to upgrade BIOS and we fail.
*/
#define ALI15X3_SMB_DEFAULTBASE 0xE800
/* ALI15X3 address lock bits */
#define ALI15X3_LOCK 0x06
/* ALI15X3 command constants */
#define ALI15X3_ABORT 0x02
#define ALI15X3_T_OUT 0x04
#define ALI15X3_QUICK 0x00
#define ALI15X3_BYTE 0x10
#define ALI15X3_BYTE_DATA 0x20
#define ALI15X3_WORD_DATA 0x30
#define ALI15X3_BLOCK_DATA 0x40
#define ALI15X3_BLOCK_CLR 0x80
/* ALI15X3 status register bits */
#define ALI15X3_STS_IDLE 0x04
#define ALI15X3_STS_BUSY 0x08
#define ALI15X3_STS_DONE 0x10
#define ALI15X3_STS_DEV 0x20 /* device error */
#define ALI15X3_STS_COLL 0x40 /* collision or no response */
#define ALI15X3_STS_TERM 0x80 /* terminated by abort */
#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */
/* If force_addr is set to anything different from 0, we forcibly enable
the device at the given address. */
static int force_addr = 0;
MODULE_PARM(force_addr, "i");
MODULE_PARM_DESC(force_addr,
"Initialize the base address of the i2c controller");
static struct pci_driver ali15x3_driver;
static unsigned short ali15x3_smba = 0;
static int ali15x3_setup(struct pci_dev *ALI15X3_dev)
{
u16 a;
unsigned char temp;
/* Check the following things:
- SMB I/O address is initialized
- Device is enabled
- We can use the addresses
*/
/* Unlock the register.
The data sheet says that the address registers are read-only
if the lock bits are 1, but in fact the address registers
are zero unless you clear the lock bits.
*/
pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp);
if (temp & ALI15X3_LOCK) {
temp &= ~ALI15X3_LOCK;
pci_write_config_byte(ALI15X3_dev, SMBATPC, temp);
}
/* Determine the address of the SMBus area */
pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba);
ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1));
if (ali15x3_smba == 0 && force_addr == 0) {
dev_err(ALI15X3_dev, "ALI15X3_smb region uninitialized "
"- upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
if(force_addr)
ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1);
if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE,
ali15x3_driver.name)) {
dev_err(ALI15X3_dev,
"ALI15X3_smb region 0x%x already in use!\n",
ali15x3_smba);
return -ENODEV;
}
if(force_addr) {
dev_info(ALI15X3_dev, "forcing ISA address 0x%04X\n",
ali15x3_smba);
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba))
return -ENODEV;
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(ALI15X3_dev, SMBBA, &a))
return -ENODEV;
if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) {
/* make sure it works */
dev_err(ALI15X3_dev,
"force address failed - not supported?\n");
return -ENODEV;
}
}
/* check if whole device is enabled */
pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp);
if ((temp & 1) == 0) {
dev_info(ALI15X3_dev, "enabling SMBus device\n");
pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01);
}
/* Is SMB Host controller enabled? */
pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp);
if ((temp & 1) == 0) {
dev_info(ALI15X3_dev, "enabling SMBus controller\n");
pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01);
}
/* set SMB clock to 74KHz as recommended in data sheet */
pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20);
/*
The interrupt routing for SMB is set up in register 0x77 in the
1533 ISA Bridge device, NOT in the 7101 device.
Don't bother with finding the 1533 device and reading the register.
if ((....... & 0x0F) == 1)
dev_dbg(ALI15X3_dev, "ALI15X3 using Interrupt 9 for SMBus.\n");
*/
pci_read_config_byte(ALI15X3_dev, SMBREV, &temp);
dev_dbg(ALI15X3_dev, "SMBREV = 0x%X\n", temp);
dev_dbg(ALI15X3_dev, "iALI15X3_smba = 0x%X\n", ali15x3_smba);
return 0;
}
/* Another internally used function */
static int ali15x3_transaction(struct i2c_adapter *adap)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(adap, "Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS),
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
/* get status */
temp = inb_p(SMBHSTSTS);
/* Make sure the SMBus host is ready to start transmitting */
/* Check the busy bit first */
if (temp & ALI15X3_STS_BUSY) {
/*
If the host controller is still busy, it may have timed out in the
previous transaction, resulting in a "SMBus Timeout" Dev.
I've tried the following to reset a stuck busy bit.
1. Reset the controller with an ABORT command.
(this doesn't seem to clear the controller if an external
device is hung)
2. Reset the controller and the other SMBus devices with a
T_OUT command. (this clears the host busy bit if an
external device is hung, but it comes back upon a new access
to a device)
3. Disable and reenable the controller in SMBHSTCFG
Worst case, nothing seems to work except power reset.
*/
/* Abort - reset the host controller */
/*
Try resetting entire SMB bus, including other devices -
This may not work either - it clears the BUSY bit but
then the BUSY bit may come back on when you try and use the chip again.
If that's the case you are stuck.
*/
dev_info(adap, "Resetting entire SMB Bus to "
"clear busy condition (%02x)\n", temp);
outb_p(ALI15X3_T_OUT, SMBHSTCNT);
temp = inb_p(SMBHSTSTS);
}
/* now check the error bits and the busy bit */
if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) {
/* do a clear-on-write */
outb_p(0xFF, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) &
(ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) {
/* this is probably going to be correctable only by a power reset
as one of the bits now appears to be stuck */
/* This may be a bus or device with electrical problems. */
dev_err(adap, "SMBus reset failed! (0x%02x) - "
"controller or device on bus is probably hung\n",
temp);
return -1;
}
} else {
/* check and clear done bit */
if (temp & ALI15X3_STS_DONE) {
outb_p(temp, SMBHSTSTS);
}
}
/* start the transaction by writing anything to the start register */
outb_p(0xFF, SMBHSTSTART);
/* We will always wait for a fraction of a second! */
timeout = 0;
do {
i2c_delay(1);
temp = inb_p(SMBHSTSTS);
} while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE)))
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
result = -1;
dev_err(adap, "SMBus Timeout!\n");
}
if (temp & ALI15X3_STS_TERM) {
result = -1;
dev_dbg(adap, "Error: Failed bus transaction\n");
}
/*
Unfortunately the ALI SMB controller maps "no response" and "bus
collision" into a single bit. No reponse is the usual case so don't
do a printk.
This means that bus collisions go unreported.
*/
if (temp & ALI15X3_STS_COLL) {
result = -1;
dev_dbg(adap,
"Error: no response or bus collision ADD=%02x\n",
inb_p(SMBHSTADD));
}
/* haven't ever seen this */
if (temp & ALI15X3_STS_DEV) {
result = -1;
dev_err(adap, "Error: device error\n");
}
dev_dbg(adap, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS),
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
return result;
}
/* Return -1 on error. */
static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data * data)
{
int i, len;
int temp;
int timeout;
/* clear all the bits (clear-on-write) */
outb_p(0xFF, SMBHSTSTS);
/* make sure SMBus is idle */
temp = inb_p(SMBHSTSTS);
for (timeout = 0;
(timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE);
timeout++) {
i2c_delay(1);
temp = inb_p(SMBHSTSTS);
}
if (timeout >= MAX_TIMEOUT) {
dev_err(adap, "Idle wait Timeout! STS=0x%02x\n", temp);
}
switch (size) {
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = ALI15X3_QUICK;
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
size = ALI15X3_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
size = ALI15X3_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
size = ALI15X3_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0) {
len = 0;
data->block[0] = len;
}
if (len > 32) {
len = 32;
data->block[0] = len;
}
outb_p(len, SMBHSTDAT0);
/* Reset SMBBLKDAT */
outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);
for (i = 1; i <= len; i++)
outb_p(data->block[i], SMBBLKDAT);
}
size = ALI15X3_BLOCK_DATA;
break;
default:
printk
(KERN_WARNING "i2c-ali15x3.o: Unsupported transaction %d\n", size);
return -1;
}
outb_p(size, SMBHSTCNT); /* output command */
if (ali15x3_transaction(adap)) /* Error in transaction */
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK))
return 0;
switch (size) {
case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */
data->byte = inb_p(SMBHSTDAT0);
break;
case ALI15X3_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case ALI15X3_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
case ALI15X3_BLOCK_DATA:
len = inb_p(SMBHSTDAT0);
if (len > 32)
len = 32;
data->block[0] = len;
/* Reset SMBBLKDAT */
outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT);
for (i = 1; i <= data->block[0]; i++) {
data->block[i] = inb_p(SMBBLKDAT);
dev_dbg(adap, "Blk: len=%d, i=%d, data=%02x\n",
len, i, data->block[i]);
}
break;
}
return 0;
}
static void ali15x3_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void ali15x3_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static u32 ali15x3_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = ali15x3_access,
.functionality = ali15x3_func,
};
static struct i2c_adapter ali15x3_adapter = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3,
.algo = &smbus_algorithm,
.inc_use = ali15x3_inc,
.dec_use = ali15x3_dec,
};
static struct pci_device_id ali15x3_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_AL,
.device = PCI_DEVICE_ID_AL_M7101,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (ali15x3_setup(dev)) {
dev_err(dev,
"ALI15X3 not detected, module not inserted.\n");
return -ENODEV;
}
snprintf(ali15x3_adapter.name, 32,
"SMBus ALI15X3 adapter at %04x", ali15x3_smba);
return i2c_add_adapter(&ali15x3_adapter);
}
static void __devexit ali15x3_remove(struct pci_dev *dev)
{
i2c_del_adapter(&ali15x3_adapter);
release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE);
}
static struct pci_driver ali15x3_driver = {
.name = "ali15x3 smbus",
.id_table = ali15x3_ids,
.probe = ali15x3_probe,
.remove = __devexit_p(ali15x3_remove),
};
static int __init i2c_ali15x3_init(void)
{
printk("i2c-ali15x3.o version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&ali15x3_driver);
}
static void __exit i2c_ali15x3_exit(void)
{
pci_unregister_driver(&ali15x3_driver);
}
MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("ALI15X3 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_ali15x3_init);
module_exit(i2c_ali15x3_exit);
-264
View File
@@ -1,264 +0,0 @@
/*
* i2c-amd756-s4882.c - i2c-amd756 extras for the Tyan S4882 motherboard
*
* Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
*
* 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.
*/
/*
* We select the channels by sending commands to the Philips
* PCA9556 chip at I2C address 0x18. The main adapter is used for
* the non-multiplexed part of the bus, and 4 virtual adapters
* are defined for the multiplexed addresses: 0x50-0x53 (memory
* module EEPROM) located on channels 1-4, and 0x4c (LM63)
* located on multiplexed channels 0 and 5-7. We define one
* virtual adapter per CPU, which corresponds to two multiplexed
* channels:
* CPU0: virtual adapter 1, channels 1 and 0
* CPU1: virtual adapter 2, channels 2 and 5
* CPU2: virtual adapter 3, channels 3 and 6
* CPU3: virtual adapter 4, channels 4 and 7
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <asm/semaphore.h>
#define DRV_NAME "i2c-amd756-s4882"
extern struct i2c_adapter amd756_smbus;
static struct i2c_adapter *s4882_adapter;
static struct i2c_algorithm *s4882_algo;
/* Wrapper access functions for multiplexed SMBus */
static DECLARE_MUTEX(amd756_lock);
static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
int error;
/* We exclude the multiplexed addresses */
if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
|| addr == 0x18)
return -1;
down(&amd756_lock);
error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
command, size, data);
up(&amd756_lock);
return error;
}
/* We remember the last used channels combination so as to only switch
channels when it is really needed. This greatly reduces the SMBus
overhead, but also assumes that nobody will be writing to the PCA9556
in our back. */
static u8 last_channels;
static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data,
u8 channels)
{
int error;
/* We exclude the non-multiplexed addresses */
if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
return -1;
down(&amd756_lock);
if (last_channels != channels) {
union i2c_smbus_data mplxdata;
mplxdata.byte = channels;
error = amd756_smbus.algo->smbus_xfer(adap, 0x18, 0,
I2C_SMBUS_WRITE, 0x01,
I2C_SMBUS_BYTE_DATA,
&mplxdata);
if (error)
goto UNLOCK;
last_channels = channels;
}
error = amd756_smbus.algo->smbus_xfer(adap, addr, flags, read_write,
command, size, data);
UNLOCK:
up(&amd756_lock);
return error;
}
static s32 amd756_access_virt1(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
/* CPU0: channels 1 and 0 enabled */
return amd756_access_channel(adap, addr, flags, read_write, command,
size, data, 0x03);
}
static s32 amd756_access_virt2(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
/* CPU1: channels 2 and 5 enabled */
return amd756_access_channel(adap, addr, flags, read_write, command,
size, data, 0x24);
}
static s32 amd756_access_virt3(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
/* CPU2: channels 3 and 6 enabled */
return amd756_access_channel(adap, addr, flags, read_write, command,
size, data, 0x48);
}
static s32 amd756_access_virt4(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size,
union i2c_smbus_data * data)
{
/* CPU3: channels 4 and 7 enabled */
return amd756_access_channel(adap, addr, flags, read_write, command,
size, data, 0x90);
}
static int __init amd756_s4882_init(void)
{
int i, error;
union i2c_smbus_data ioconfig;
/* Unregister physical bus */
error = i2c_del_adapter(&amd756_smbus);
if (error) {
if (error != -ENODEV)
printk(KERN_ERR DRV_NAME ": Physical bus removal "
"failed\n");
goto ERROR0;
}
printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4882\n");
/* Define the 5 virtual adapters and algorithms structures */
if (!(s4882_adapter = kmalloc(5 * sizeof(struct i2c_adapter),
GFP_KERNEL))) {
error = -ENOMEM;
goto ERROR1;
}
if (!(s4882_algo = kmalloc(5 * sizeof(struct i2c_algorithm),
GFP_KERNEL))) {
error = -ENOMEM;
goto ERROR2;
}
/* Fill in the new structures */
s4882_algo[0] = *(amd756_smbus.algo);
s4882_algo[0].smbus_xfer = amd756_access_virt0;
s4882_adapter[0] = amd756_smbus;
s4882_adapter[0].algo = s4882_algo;
for (i = 1; i < 5; i++) {
s4882_algo[i] = *(amd756_smbus.algo);
s4882_adapter[i] = amd756_smbus;
sprintf(s4882_adapter[i].name,
"SMBus 8111 adapter (CPU%d)", i-1);
s4882_adapter[i].algo = s4882_algo+i;
}
s4882_algo[1].smbus_xfer = amd756_access_virt1;
s4882_algo[2].smbus_xfer = amd756_access_virt2;
s4882_algo[3].smbus_xfer = amd756_access_virt3;
s4882_algo[4].smbus_xfer = amd756_access_virt4;
/* Configure the PCA9556 multiplexer */
ioconfig.byte = 0x00; /* All I/O to output mode */
error = amd756_smbus.algo->smbus_xfer(&amd756_smbus, 0x18, 0,
I2C_SMBUS_WRITE, 0x03,
I2C_SMBUS_BYTE_DATA, &ioconfig);
if (error) {
printk(KERN_ERR DRV_NAME ": PCA9556 configuration failed\n");
error = -EIO;
goto ERROR3;
}
/* Register virtual adapters */
for (i = 0; i < 5; i++) {
error = i2c_add_adapter(s4882_adapter+i);
if (error) {
printk(KERN_ERR DRV_NAME
": Virtual adapter %d registration "
"failed, module not inserted\n", i);
for (i--; i >= 0; i--)
i2c_del_adapter(s4882_adapter+i);
goto ERROR3;
}
}
return 0;
ERROR3:
kfree(s4882_algo);
s4882_algo = NULL;
ERROR2:
kfree(s4882_adapter);
s4882_adapter = NULL;
ERROR1:
i2c_del_adapter(&amd756_smbus);
ERROR0:
return error;
}
static void __exit amd756_s4882_exit(void)
{
if (s4882_adapter) {
int i;
for (i = 0; i < 5; i++)
i2c_del_adapter(s4882_adapter+i);
kfree(s4882_adapter);
s4882_adapter = NULL;
}
if (s4882_algo) {
kfree(s4882_algo);
s4882_algo = NULL;
}
/* Restore physical bus */
if (i2c_add_adapter(&amd756_smbus))
printk(KERN_ERR DRV_NAME ": Physical bus restoration "
"failed\n");
}
MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("S4882 SMBus multiplexing");
MODULE_LICENSE("GPL");
module_init(amd756_s4882_init);
module_exit(amd756_s4882_exit);
-444
View File
@@ -1,444 +0,0 @@
/*
amd756.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
Shamelessly ripped from i2c-piix4.c:
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com>
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.
*/
/*
2002-04-08: Added nForce support. (Csaba Halasz)
2002-10-03: Fixed nForce PnP I/O port. (Michael Steil)
2002-12-28: Rewritten into something that resembles a Linux driver (hch)
2003-11-29: Added back AMD8111 removed by the previous rewrite.
(Philip Pokorny)
2004-02-15: Don't register driver to avoid driver conflicts.
(Daniel Rune Jensen)
*/
/*
Supports AMD756, AMD766, AMD768, AMD8111 and nVidia nForce
Note: we assume there can only be one device, with one SMBus interface.
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
#include "version.h"
#define DRV_NAME "i2c-amd756"
/* AMD756 SMBus address offsets */
#define SMB_ADDR_OFFSET 0xE0
#define SMB_IOSIZE 16
#define SMB_GLOBAL_STATUS (0x0 + amd756_ioport)
#define SMB_GLOBAL_ENABLE (0x2 + amd756_ioport)
#define SMB_HOST_ADDRESS (0x4 + amd756_ioport)
#define SMB_HOST_DATA (0x6 + amd756_ioport)
#define SMB_HOST_COMMAND (0x8 + amd756_ioport)
#define SMB_HOST_BLOCK_DATA (0x9 + amd756_ioport)
#define SMB_HAS_DATA (0xA + amd756_ioport)
#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_ioport)
#define SMB_HAS_HOST_ADDRESS (0xE + amd756_ioport)
#define SMB_SNOOP_ADDRESS (0xF + amd756_ioport)
/* PCI Address Constants */
/* address of I/O space */
#define SMBBA 0x058 /* mh */
#define SMBBANFORCE 0x014
/* general configuration */
#define SMBGCFG 0x041 /* mh */
/* silicon revision code */
#define SMBREV 0x008
/* Other settings */
#define MAX_TIMEOUT 500
/* AMD756 constants */
#define AMD756_QUICK 0x00
#define AMD756_BYTE 0x01
#define AMD756_BYTE_DATA 0x02
#define AMD756_WORD_DATA 0x03
#define AMD756_PROCESS_CALL 0x04
#define AMD756_BLOCK_DATA 0x05
static unsigned short amd756_ioport = 0;
/*
SMBUS event = I/O 28-29 bit 11
see E0 for the status bits and enabled in E2
*/
#define GS_ABRT_STS (1 << 0)
#define GS_COL_STS (1 << 1)
#define GS_PRERR_STS (1 << 2)
#define GS_HST_STS (1 << 3)
#define GS_HCYC_STS (1 << 4)
#define GS_TO_STS (1 << 5)
#define GS_SMB_STS (1 << 11)
#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \
GS_HCYC_STS | GS_TO_STS )
#define GE_CYC_TYPE_MASK (7)
#define GE_HOST_STC (1 << 3)
#define GE_ABORT (1 << 5)
static int amd756_transaction(void)
{
int temp;
int result = 0;
int timeout = 0;
pr_debug(DRV_NAME
": Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
/* Make sure the SMBus host is ready to start transmitting */
if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) {
pr_debug(DRV_NAME ": SMBus busy (%04x). Waiting...\n", temp);
do {
i2c_delay(1);
temp = inw_p(SMB_GLOBAL_STATUS);
} while ((temp & (GS_HST_STS | GS_SMB_STS)) &&
(timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
pr_debug(DRV_NAME ": Busy wait timeout (%04x)\n", temp);
goto abort;
}
timeout = 0;
}
/* start the transaction by setting the start bit */
outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE);
/* We will always wait for a fraction of a second! */
do {
i2c_delay(1);
temp = inw_p(SMB_GLOBAL_STATUS);
} while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
pr_debug(DRV_NAME ": Completion timeout!\n");
goto abort;
}
if (temp & GS_PRERR_STS) {
result = -1;
pr_debug(DRV_NAME ": SMBus Protocol error (no response)!\n");
}
if (temp & GS_COL_STS) {
result = -1;
printk(KERN_WARNING DRV_NAME ": SMBus collision!\n");
}
if (temp & GS_TO_STS) {
result = -1;
pr_debug(DRV_NAME ": SMBus protocol timeout!\n");
}
if (temp & GS_HCYC_STS)
pr_debug(DRV_NAME ": SMBus protocol success!\n");
outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
#ifdef DEBUG
if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) {
pr_debug(DRV_NAME
": Failed reset at end of transaction (%04x)\n", temp);
}
pr_debug(DRV_NAME
": Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n",
inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE),
inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA));
#endif
return result;
abort:
printk(KERN_WARNING DRV_NAME ": Sending abort.\n");
outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE);
i2c_delay(100);
outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
return -1;
}
/* Return -1 on error. */
static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
int i, len;
/** TODO: Should I supporte the 10-bit transfers? */
switch (size) {
/* TODO: proc call is supported, I'm just not sure what to do here... */
case I2C_SMBUS_QUICK:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
size = AMD756_QUICK;
break;
case I2C_SMBUS_BYTE:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMB_HOST_DATA);
size = AMD756_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
outb_p(command, SMB_HOST_COMMAND);
if (read_write == I2C_SMBUS_WRITE)
outw_p(data->byte, SMB_HOST_DATA);
size = AMD756_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
outb_p(command, SMB_HOST_COMMAND);
if (read_write == I2C_SMBUS_WRITE)
outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */
size = AMD756_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
outb_p(command, SMB_HOST_COMMAND);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0)
len = 0;
if (len > 32)
len = 32;
outw_p(len, SMB_HOST_DATA);
/* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
for (i = 1; i <= len; i++)
outb_p(data->block[i],
SMB_HOST_BLOCK_DATA);
}
size = AMD756_BLOCK_DATA;
break;
default:
printk
(KERN_WARNING "i2c-amd756.o: Unsupported transaction %d\n", size);
return -1;
}
/* How about enabling interrupts... */
outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE);
if (amd756_transaction()) /* Error in transaction */
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK))
return 0;
switch (size) {
case AMD756_BYTE:
data->byte = inw_p(SMB_HOST_DATA);
break;
case AMD756_BYTE_DATA:
data->byte = inw_p(SMB_HOST_DATA);
break;
case AMD756_WORD_DATA:
data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */
break;
case AMD756_BLOCK_DATA:
data->block[0] = inw_p(SMB_HOST_DATA) & 0x3f;
if(data->block[0] > 32)
data->block[0] = 32;
/* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */
for (i = 1; i <= data->block[0]; i++)
data->block[i] = inb_p(SMB_HOST_BLOCK_DATA);
break;
}
return 0;
}
static void amd756_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void amd756_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static u32 amd756_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = amd756_access,
.functionality = amd756_func,
};
struct i2c_adapter amd756_smbus = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756,
.algo = &smbus_algorithm,
.inc_use = amd756_inc,
.dec_use = amd756_dec,
};
enum chiptype { AMD756, AMD766, AMD768, NFORCE, AMD8111 };
static const char* chipname[] = {
"AMD756", "AMD766", "AMD768",
"nVidia nForce", "AMD8111",
};
static struct pci_device_id amd756_ids[] __devinitdata = {
{PCI_VENDOR_ID_AMD, 0x740B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD756 },
{PCI_VENDOR_ID_AMD, 0x7413, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD766 },
{PCI_VENDOR_ID_AMD, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD768 },
{PCI_VENDOR_ID_AMD, 0x746B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AMD8111 },
{PCI_VENDOR_ID_NVIDIA, 0x01B4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, NFORCE },
{ 0, }
};
static int __devinit amd756_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
int nforce = (id->driver_data == NFORCE);
int error;
u8 temp;
if (amd756_ioport) {
printk(KERN_ERR DRV_NAME ": Only one device supported. "
"(you have a strange motherboard, btw..)\n");
return -ENODEV;
}
if (nforce) {
if (PCI_FUNC(pdev->devfn) != 1)
return -ENODEV;
pci_read_config_word(pdev, SMBBANFORCE, &amd756_ioport);
amd756_ioport &= 0xfffc;
} else { /* amd */
if (PCI_FUNC(pdev->devfn) != 3)
return -ENODEV;
pci_read_config_byte(pdev, SMBGCFG, &temp);
if ((temp & 128) == 0) {
printk(KERN_ERR DRV_NAME
": Error: SMBus controller I/O not enabled!\n");
return -ENODEV;
}
/* Determine the address of the SMBus areas */
/* Technically it is a dword but... */
pci_read_config_word(pdev, SMBBA, &amd756_ioport);
amd756_ioport &= 0xff00;
amd756_ioport += SMB_ADDR_OFFSET;
}
if (!request_region(amd756_ioport, SMB_IOSIZE, "amd756-smbus")) {
printk(KERN_ERR DRV_NAME
": SMB region 0x%x already in use!\n", amd756_ioport);
return -ENODEV;
}
#ifdef DEBUG
pci_read_config_byte(pdev, SMBREV, &temp);
printk(KERN_DEBUG DRV_NAME ": SMBREV = 0x%X\n", temp);
printk(KERN_DEBUG DRV_NAME ": AMD756_smba = 0x%X\n", amd756_ioport);
#endif
sprintf(amd756_smbus.name, "SMBus %s adapter at %04x",
chipname[id->driver_data], amd756_ioport);
error = i2c_add_adapter(&amd756_smbus);
if (error) {
printk(KERN_ERR DRV_NAME
": Adapter registration failed, module not inserted.\n");
goto out_err;
}
return 0;
out_err:
release_region(amd756_ioport, SMB_IOSIZE);
return error;
}
static int __init i2c_amd756_init(void)
{
struct pci_dev *dev;
const struct pci_device_id *id;
printk(KERN_INFO "i2c-amd756.o version %s (%s)\n", LM_VERSION, LM_DATE);
pci_for_each_dev(dev) {
id = pci_match_device(amd756_ids, dev);
if (id && amd756_probe(dev, id) >= 0)
return 0;
}
return -ENODEV;
}
static void __exit i2c_amd756_exit(void)
{
i2c_del_adapter(&amd756_smbus);
release_region(amd756_ioport, SMB_IOSIZE);
}
MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>");
MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(amd756_smbus);
module_init(i2c_amd756_init)
module_exit(i2c_amd756_exit)
-424
View File
@@ -1,424 +0,0 @@
/*
* SMBus 2.0 driver for AMD-8111 IO-Hub.
*
* Copyright (c) 2002 Vojtech Pavlik
*
* 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 version 2.
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
#ifndef I2C_HW_SMBUS_AMD8111
#error Your i2c is too old - i2c-2.7.0 or greater required!
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver");
struct amd_smbus {
struct pci_dev *dev;
struct i2c_adapter adapter;
int base;
int size;
};
static struct pci_driver amd8111_driver;
/*
* AMD PCI control registers definitions.
*/
#define AMD_PCI_MISC 0x48
#define AMD_PCI_MISC_SCI 0x04 /* deliver SCI */
#define AMD_PCI_MISC_INT 0x02 /* deliver PCI IRQ */
#define AMD_PCI_MISC_SPEEDUP 0x01 /* 16x clock speedup */
/*
* ACPI 2.0 chapter 13 PCI interface definitions.
*/
#define AMD_EC_DATA 0x00 /* data register */
#define AMD_EC_SC 0x04 /* status of controller */
#define AMD_EC_CMD 0x04 /* command register */
#define AMD_EC_ICR 0x08 /* interrupt control register */
#define AMD_EC_SC_SMI 0x04 /* smi event pending */
#define AMD_EC_SC_SCI 0x02 /* sci event pending */
#define AMD_EC_SC_BURST 0x01 /* burst mode enabled */
#define AMD_EC_SC_CMD 0x08 /* byte in data reg is command */
#define AMD_EC_SC_IBF 0x02 /* data ready for embedded controller */
#define AMD_EC_SC_OBF 0x01 /* data ready for host */
#define AMD_EC_CMD_RD 0x80 /* read EC */
#define AMD_EC_CMD_WR 0x81 /* write EC */
#define AMD_EC_CMD_BE 0x82 /* enable burst mode */
#define AMD_EC_CMD_BD 0x83 /* disable burst mode */
#define AMD_EC_CMD_QR 0x84 /* query EC */
/*
* ACPI 2.0 chapter 13 access of registers of the EC
*/
unsigned int amd_ec_wait_write(struct amd_smbus *smbus)
{
int timeout = 500;
while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF))
udelay(1);
if (!timeout) {
printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for IBF to clear\n");
return -1;
}
return 0;
}
unsigned int amd_ec_wait_read(struct amd_smbus *smbus)
{
int timeout = 500;
while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF))
udelay(1);
if (!timeout) {
printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for OBF to set\n");
return -1;
}
return 0;
}
unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data)
{
if (amd_ec_wait_write(smbus))
return -1;
outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD);
if (amd_ec_wait_write(smbus))
return -1;
outb(address, smbus->base + AMD_EC_DATA);
if (amd_ec_wait_read(smbus))
return -1;
*data = inb(smbus->base + AMD_EC_DATA);
return 0;
}
unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data)
{
if (amd_ec_wait_write(smbus))
return -1;
outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD);
if (amd_ec_wait_write(smbus))
return -1;
outb(address, smbus->base + AMD_EC_DATA);
if (amd_ec_wait_write(smbus))
return -1;
outb(data, smbus->base + AMD_EC_DATA);
return 0;
}
/*
* ACPI 2.0 chapter 13 SMBus 2.0 EC register model
*/
#define AMD_SMB_PRTCL 0x00 /* protocol, PEC */
#define AMD_SMB_STS 0x01 /* status */
#define AMD_SMB_ADDR 0x02 /* address */
#define AMD_SMB_CMD 0x03 /* command */
#define AMD_SMB_DATA 0x04 /* 32 data registers */
#define AMD_SMB_BCNT 0x24 /* number of data bytes */
#define AMD_SMB_ALRM_A 0x25 /* alarm address */
#define AMD_SMB_ALRM_D 0x26 /* 2 bytes alarm data */
#define AMD_SMB_STS_DONE 0x80
#define AMD_SMB_STS_ALRM 0x40
#define AMD_SMB_STS_RES 0x20
#define AMD_SMB_STS_STATUS 0x1f
#define AMD_SMB_STATUS_OK 0x00
#define AMD_SMB_STATUS_FAIL 0x07
#define AMD_SMB_STATUS_DNAK 0x10
#define AMD_SMB_STATUS_DERR 0x11
#define AMD_SMB_STATUS_CMD_DENY 0x12
#define AMD_SMB_STATUS_UNKNOWN 0x13
#define AMD_SMB_STATUS_ACC_DENY 0x17
#define AMD_SMB_STATUS_TIMEOUT 0x18
#define AMD_SMB_STATUS_NOTSUP 0x19
#define AMD_SMB_STATUS_BUSY 0x1A
#define AMD_SMB_STATUS_PEC 0x1F
#define AMD_SMB_PRTCL_WRITE 0x00
#define AMD_SMB_PRTCL_READ 0x01
#define AMD_SMB_PRTCL_QUICK 0x02
#define AMD_SMB_PRTCL_BYTE 0x04
#define AMD_SMB_PRTCL_BYTE_DATA 0x06
#define AMD_SMB_PRTCL_WORD_DATA 0x08
#define AMD_SMB_PRTCL_BLOCK_DATA 0x0a
#define AMD_SMB_PRTCL_PROC_CALL 0x0c
#define AMD_SMB_PRTCL_BLOCK_PROC_CALL 0x0d
#define AMD_SMB_PRTCL_I2C_BLOCK_DATA 0x4a
#define AMD_SMB_PRTCL_PEC 0x80
s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size, union i2c_smbus_data * data)
{
struct amd_smbus *smbus = adap->algo_data;
unsigned char protocol, len, pec, temp[2];
int i;
protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ : AMD_SMB_PRTCL_WRITE;
pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0;
switch (size) {
case I2C_SMBUS_QUICK:
protocol |= AMD_SMB_PRTCL_QUICK;
read_write = I2C_SMBUS_WRITE;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE)
amd_ec_write(smbus, AMD_SMB_CMD, command);
protocol |= AMD_SMB_PRTCL_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
amd_ec_write(smbus, AMD_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE)
amd_ec_write(smbus, AMD_SMB_DATA, data->byte);
protocol |= AMD_SMB_PRTCL_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
amd_ec_write(smbus, AMD_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
amd_ec_write(smbus, AMD_SMB_DATA, data->word & 0xff);
amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8);
}
protocol |= AMD_SMB_PRTCL_WORD_DATA | pec;
break;
case I2C_SMBUS_BLOCK_DATA:
amd_ec_write(smbus, AMD_SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX);
amd_ec_write(smbus, AMD_SMB_BCNT, len);
for (i = 0; i < len; i++)
amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
}
protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX);
amd_ec_write(smbus, AMD_SMB_CMD, command);
amd_ec_write(smbus, AMD_SMB_BCNT, len);
if (read_write == I2C_SMBUS_WRITE)
for (i = 0; i < len; i++)
amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA;
break;
case I2C_SMBUS_PROC_CALL:
amd_ec_write(smbus, AMD_SMB_CMD, command);
amd_ec_write(smbus, AMD_SMB_DATA, data->word & 0xff);
amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8);
protocol = AMD_SMB_PRTCL_PROC_CALL | pec;
read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX - 1);
amd_ec_write(smbus, AMD_SMB_CMD, command);
amd_ec_write(smbus, AMD_SMB_BCNT, len);
for (i = 0; i < len; i++)
amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]);
protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec;
read_write = I2C_SMBUS_READ;
break;
default:
printk(KERN_WARNING "i2c-amd8111.c: Unsupported transaction %d\n", size);
return -1;
}
amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
amd_ec_write(smbus, AMD_SMB_PRTCL, protocol);
amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
if (~temp[0] & AMD_SMB_STS_DONE) {
udelay(500);
amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
}
if (~temp[0] & AMD_SMB_STS_DONE) {
i2c_delay(HZ/100);
amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
}
if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS))
return -1;
if (read_write == I2C_SMBUS_WRITE)
return 0;
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
amd_ec_read(smbus, AMD_SMB_DATA, &data->byte);
break;
case I2C_SMBUS_WORD_DATA:
case I2C_SMBUS_PROC_CALL:
amd_ec_read(smbus, AMD_SMB_DATA, temp + 0);
amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1);
data->word = (temp[1] << 8) | temp[0];
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_BLOCK_PROC_CALL:
amd_ec_read(smbus, AMD_SMB_BCNT, &len);
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < len; i++)
amd_ec_read(smbus, AMD_SMB_DATA + i, data->block + i + 1);
data->block[0] = len;
break;
}
return 0;
}
static void amd8111_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void amd8111_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
u32 amd8111_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus 2.0 adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = amd8111_access,
.functionality = amd8111_func,
};
static struct pci_device_id amd8111_ids[] __devinitdata = {
{ 0x1022, 0x746a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, }
};
static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct amd_smbus *smbus;
int error;
if (!(pci_resource_flags(dev, 0) & IORESOURCE_IO))
return -ENODEV;
if (!(smbus = kmalloc(sizeof(struct amd_smbus), GFP_KERNEL)))
return -ENOMEM;
memset(smbus, 0, sizeof(struct amd_smbus));
pci_set_drvdata(dev, smbus);
smbus->dev = dev;
smbus->base = pci_resource_start(dev, 0);
smbus->size = pci_resource_len(dev, 0);
if (!request_region(smbus->base, smbus->size, amd8111_driver.name)) {
kfree(smbus);
return -EBUSY;
}
sprintf(smbus->adapter.name, "SMBus2 AMD8111 adapter at %04x", smbus->base);
smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD8111;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
smbus->adapter.inc_use = amd8111_inc;
smbus->adapter.dec_use = amd8111_dec;
pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0);
error = i2c_add_adapter(&smbus->adapter);
if (error) {
printk(KERN_WARNING "i2c-amd8111.c: Failed to register adapter.\n");
release_region(smbus->base, smbus->size);
kfree(smbus);
return error;
}
printk(KERN_INFO "i2c-amd8111.c: AMD8111 SMBus 2.0 adapter at %#x\n", smbus->base);
return 0;
}
static void __devexit amd8111_remove(struct pci_dev *dev)
{
struct amd_smbus *smbus = (void*) pci_get_drvdata(dev);
i2c_del_adapter(&smbus->adapter);
release_region(smbus->base, smbus->size);
kfree(smbus);
}
static struct pci_driver amd8111_driver = {
.name = "amd8111 smbus 2.0",
.id_table = amd8111_ids,
.probe = amd8111_probe,
.remove = __devexit_p(amd8111_remove),
};
static int __init i2c_amd8111_init(void)
{
printk(KERN_INFO "i2c-amd8111.o version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&amd8111_driver);
}
static void __exit i2c_amd8111_exit(void)
{
pci_unregister_driver(&amd8111_driver);
}
module_init(i2c_amd8111_init);
module_exit(i2c_amd8111_exit);
-191
View File
@@ -1,191 +0,0 @@
/*
i2c-hydra.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
i2c Support for the Apple `Hydra' Mac I/O
Copyright (c) 1999 Geert Uytterhoeven <geert@linux-m68k.org>
Based on i2c Support for Via Technologies 82C586B South Bridge
Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi>
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 <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/param.h> /* for HZ */
#include "sensors_compat.h"
MODULE_LICENSE("GPL");
#define HYDRA_CACHE_PD 0x00000030
#define HYDRA_CPD_PD0 0x00000001 /* CachePD lines */
#define HYDRA_CPD_PD1 0x00000002
#define HYDRA_CPD_PD2 0x00000004
#define HYDRA_CPD_PD3 0x00000008
#define HYDRA_SCLK HYDRA_CPD_PD0
#define HYDRA_SDAT HYDRA_CPD_PD1
#define HYDRA_SCLK_OE 0x00000010
#define HYDRA_SDAT_OE 0x00000020
static unsigned long hydra_base;
static inline void pdregw(u32 val)
{
writel(val, hydra_base + HYDRA_CACHE_PD);
}
static inline u32 pdregr(void)
{
u32 val = readl(hydra_base + HYDRA_CACHE_PD);
return val;
}
static void bit_hydra_setscl(void *data, int state)
{
u32 val = pdregr();
if (state)
val &= ~HYDRA_SCLK_OE;
else {
val &= ~HYDRA_SCLK;
val |= HYDRA_SCLK_OE;
}
pdregw(val);
pdregr(); /* flush posted write */
}
static void bit_hydra_setsda(void *data, int state)
{
u32 val = pdregr();
if (state)
val &= ~HYDRA_SDAT_OE;
else {
val &= ~HYDRA_SDAT;
val |= HYDRA_SDAT_OE;
}
pdregw(val);
pdregr(); /* flush posted write */
}
static int bit_hydra_getscl(void *data)
{
return (pdregr() & HYDRA_SCLK) != 0;
}
static int bit_hydra_getsda(void *data)
{
return (pdregr() & HYDRA_SDAT) != 0;
}
static void bit_hydra_inc(struct i2c_adapter *adap)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void bit_hydra_dec(struct i2c_adapter *adap)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
/* ------------------------------------------------------------------------ */
static struct i2c_algo_bit_data bit_hydra_data = {
.setsda = bit_hydra_setsda,
.setscl = bit_hydra_setscl,
.getsda = bit_hydra_getsda,
.getscl = bit_hydra_getscl,
.udelay = 5,
.mdelay = 5,
.timeout = HZ
};
static struct i2c_adapter bit_hydra_ops = {
.name = "Hydra i2c",
.id = I2C_HW_B_HYDRA,
.algo_data = &bit_hydra_data,
.inc_use = bit_hydra_inc,
.dec_use = bit_hydra_dec,
};
static struct pci_device_id hydra_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_APPLE,
.device = PCI_DEVICE_ID_APPLE_HYDRA,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit hydra_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned int base_addr;
base_addr = dev->resource[0].start;
hydra_base = (unsigned long) ioremap(base_addr, 0x100);
pdregw(0); /* clear SCLK_OE and SDAT_OE */
return i2c_bit_add_bus(&bit_hydra_ops);
}
static void __devexit hydra_remove(struct pci_dev *dev)
{
pdregw(0); /* clear SCLK_OE and SDAT_OE */
i2c_bit_del_bus(&bit_hydra_ops);
iounmap((void *) hydra_base);
}
static struct pci_driver hydra_driver = {
.name = "hydra smbus",
.id_table = hydra_ids,
.probe = hydra_probe,
.remove = __devexit_p(hydra_remove),
};
static int __init i2c_hydra_init(void)
{
return pci_module_init(&hydra_driver);
}
static void __exit i2c_hydra_exit(void)
{
pci_unregister_driver(&hydra_driver);
}
MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O");
module_init(i2c_hydra_init);
module_exit(i2c_hydra_exit);
-737
View File
@@ -1,737 +0,0 @@
/*
i2c-i801.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
<mdsxyz123@yahoo.com>
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.
*/
/*
SUPPORTED DEVICES PCI ID
82801AA 2413
82801AB 2423
82801BA 2443
82801CA/CAM 2483
82801DB 24C3 (HW PEC supported, 32 byte buffer not supported)
82801EB 24D3 (HW PEC supported, 32 byte buffer not supported)
6300ESB 25A4 ("")
ICH6 266A ("")
ICH7 27DA ("")
ESB2 269B ("")
ICH8 283E ("")
ICH9 2930 ("")
This driver supports several versions of Intel's I/O Controller Hubs (ICH).
For SMBus support, they are similar to the PIIX4 and are part
of Intel's '810' and other chipsets.
See the doc/busses/i2c-i801 file for details.
I2C Block Read supported for ICH5 and higher.
Block Process Call are not supported.
*/
/* Note: we assume there can only be one I801, with one SMBus interface */
/* #define DEBUG 1 */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
/* 82801CA is undefined before kernel 2.4.13 */
#ifndef PCI_DEVICE_ID_INTEL_82801CA_3
#define PCI_DEVICE_ID_INTEL_82801CA_3 0x2483
#endif
/* 82801DB is undefined before kernel 2.4.19 */
#ifndef PCI_DEVICE_ID_INTEL_82801DB_3
#define PCI_DEVICE_ID_INTEL_82801DB_3 0x24c3
#endif
/* 82801EB is undefined before kernel 2.4.21 */
#ifndef PCI_DEVICE_ID_INTEL_82801EB_3
#define PCI_DEVICE_ID_INTEL_82801EB_3 0x24d3
#endif
/* ESB is undefined before kernel 2.4.22 */
#ifndef PCI_DEVICE_ID_INTEL_ESB_4
#define PCI_DEVICE_ID_INTEL_ESB_4 0x25a4
#endif
/* ESB2 - Enterprise Southbridge is undefined */
#ifndef PCI_DEVICE_ID_INTEL_ESB2_17
#define PCI_DEVICE_ID_INTEL_ESB2_17 0x269b
#endif
/* ICH6 is undefined */
#ifndef PCI_DEVICE_ID_INTEL_ICH6_16
#define PCI_DEVICE_ID_INTEL_ICH6_16 0x266a
#endif
/* ICH7 is undefined */
#ifndef PCI_DEVICE_ID_INTEL_ICH7_17
#define PCI_DEVICE_ID_INTEL_ICH7_17 0x27da
#endif
/* ICH8 is undefined */
#ifndef PCI_DEVICE_ID_INTEL_ICH8_5
#define PCI_DEVICE_ID_INTEL_ICH8_5 0x283e
#endif
/* ICH9 is undefined */
#ifndef PCI_DEVICE_ID_INTEL_ICH9_6
#define PCI_DEVICE_ID_INTEL_ICH9_6 0x2930
#endif
#ifdef I2C_CLIENT_PEC
#define HAVE_PEC
#endif
/* I801 SMBus address offsets */
#define SMBHSTSTS (0 + i801_smba)
#define SMBHSTCNT (2 + i801_smba)
#define SMBHSTCMD (3 + i801_smba)
#define SMBHSTADD (4 + i801_smba)
#define SMBHSTDAT0 (5 + i801_smba)
#define SMBHSTDAT1 (6 + i801_smba)
#define SMBBLKDAT (7 + i801_smba)
#define SMBPEC (8 + i801_smba) /* ICH4 only */
#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */
#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */
/* PCI Address Constants */
#define SMBBA 0x020
#define SMBHSTCFG 0x040
#define SMBREV 0x008
/* Host configuration bits for SMBHSTCFG */
#define SMBHSTCFG_HST_EN 1
#define SMBHSTCFG_SMB_SMI_EN 2
#define SMBHSTCFG_I2C_EN 4
/* Other settings */
#define MAX_TIMEOUT 100
#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */
/* I801 command constants */
#define I801_QUICK 0x00
#define I801_BYTE 0x04
#define I801_BYTE_DATA 0x08
#define I801_WORD_DATA 0x0C
#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */
#define I801_BLOCK_DATA 0x14
#define I801_I2C_BLOCK_DATA 0x18 /* ich4 and later */
#define I801_BLOCK_LAST 0x34
#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */
#define I801_START 0x40
#define I801_PEC_EN 0x80 /* ich4 and later */
/* insmod parameters */
/* If force_addr is set to anything different from 0, we forcibly enable
the I801 at the given address. VERY DANGEROUS! */
static int force_addr = 0;
MODULE_PARM(force_addr, "i");
MODULE_PARM_DESC(force_addr,
"Forcibly enable the I801 at the given address. "
"EXTREMELY DANGEROUS!");
static int i801_transaction(void);
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
int command, int hwpec);
static unsigned short i801_smba;
static struct pci_driver i801_driver;
static struct pci_dev *I801_dev;
static int isich4; /* is PEC supported? */
static int isich5; /* is i2c block read supported? */
static int __devinit i801_setup(struct pci_dev *dev)
{
unsigned char temp;
I801_dev = dev;
if (dev->device == PCI_DEVICE_ID_INTEL_82801DB_3 ||
dev->device == PCI_DEVICE_ID_INTEL_82801EB_3 ||
dev->device == PCI_DEVICE_ID_INTEL_ESB_4 ||
dev->device == PCI_DEVICE_ID_INTEL_ESB2_17 ||
dev->device == PCI_DEVICE_ID_INTEL_ICH6_16 ||
dev->device == PCI_DEVICE_ID_INTEL_ICH7_17 ||
dev->device == PCI_DEVICE_ID_INTEL_ICH8_5 ||
dev->device == PCI_DEVICE_ID_INTEL_ICH9_6)
isich4 = 1;
else
isich4 = 0;
isich5 = isich4 && dev->device != PCI_DEVICE_ID_INTEL_82801DB_3;
/* Determine the address of the SMBus area */
if (force_addr) {
i801_smba = force_addr & 0xfff0;
} else {
pci_read_config_word(I801_dev, SMBBA, &i801_smba);
i801_smba &= 0xfff0;
if(i801_smba == 0) {
dev_err(dev, "SMB base address uninitialized "
"- upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
}
if (!request_region(i801_smba, (isich4 ? 16 : 8), i801_driver.name)) {
dev_err(dev, "I801_smb region 0x%x already in use!\n",
i801_smba);
return -EBUSY;
}
pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
pci_write_config_byte(I801_dev, SMBHSTCFG, temp);
/* If force_addr is set, we program the new address here. Just to make
sure, we disable the device first. */
if (force_addr) {
pci_write_config_byte(I801_dev, SMBHSTCFG,
temp & ~SMBHSTCFG_HST_EN);
pci_write_config_word(I801_dev, SMBBA, i801_smba);
pci_write_config_byte(I801_dev, SMBHSTCFG,
temp | SMBHSTCFG_HST_EN);
dev_warn(dev, "WARNING: I801 SMBus interface set to "
"new address %04x!\n", i801_smba);
} else if (!(temp & SMBHSTCFG_HST_EN)) {
pci_write_config_byte(I801_dev, SMBHSTCFG,
temp | SMBHSTCFG_HST_EN);
dev_warn(dev, "enabling SMBus device\n");
}
if (temp & SMBHSTCFG_SMB_SMI_EN)
dev_dbg(dev, "I801 using Interrupt SMI# for SMBus.\n");
else
dev_dbg(dev, "I801 using PCI Interrupt for SMBus.\n");
pci_read_config_byte(I801_dev, SMBREV, &temp);
dev_dbg(dev, "SMBREV = 0x%X\n", temp);
dev_dbg(dev, "I801_smba = 0x%X\n", i801_smba);
return 0;
}
static int i801_transaction(void)
{
int temp;
int result = 0;
int timeout = 0;
dev_dbg(I801_dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
/* Make sure the SMBus host is ready to start transmitting */
/* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(I801_dev, "SMBus busy (%02x). Resetting...\n",
temp);
outb_p(temp, SMBHSTSTS);
if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(I801_dev, "Failed! (%02x)\n", temp);
return -1;
} else {
dev_dbg(I801_dev, "Successful!\n");
}
}
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
/* We will always wait for a fraction of a second! */
do {
i2c_delay(1);
temp = inb_p(SMBHSTSTS);
} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(I801_dev, "SMBus Timeout!\n");
result = -1;
}
if (temp & 0x10) {
result = -1;
dev_dbg(I801_dev, "Error: Failed bus transaction\n");
}
if (temp & 0x08) {
result = -1;
dev_err(I801_dev, "Bus collision! SMBus may be locked "
"until next hard reset. (sorry!)\n");
/* Clock stops and slave is stuck in mid-transmission */
}
if (temp & 0x04) {
result = -1;
dev_dbg(I801_dev, "Error: no response!\n");
}
if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00)
outb_p(inb(SMBHSTSTS), SMBHSTSTS);
if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(I801_dev, "Failed reset at end of transaction "
"(%02x)\n", temp);
}
dev_dbg(I801_dev, "Transaction (post): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
return result;
}
/* All-inclusive block transaction function */
static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
int command, int hwpec)
{
int i, len;
int smbcmd;
int temp;
int result = 0;
int timeout;
unsigned char hostc, errmask;
if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
if (read_write == I2C_SMBUS_WRITE) {
/* set I2C_EN bit in configuration register */
pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
pci_write_config_byte(I801_dev, SMBHSTCFG,
hostc | SMBHSTCFG_I2C_EN);
} else if (!isich5) {
dev_err(I801_dev,
"I2C_SMBUS_I2C_BLOCK_READ unsupported!\n");
return -1;
}
}
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 1)
len = 1;
if (len > 32)
len = 32;
outb_p(len, SMBHSTDAT0);
outb_p(data->block[1], SMBBLKDAT);
} else {
len = 32; /* max for reads */
}
if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
/* set 32 byte buffer */
}
for (i = 1; i <= len; i++) {
if (i == len && read_write == I2C_SMBUS_READ)
smbcmd = I801_BLOCK_LAST;
else if (command == I2C_SMBUS_I2C_BLOCK_DATA &&
read_write == I2C_SMBUS_READ)
smbcmd = I801_I2C_BLOCK_DATA;
else
smbcmd = I801_BLOCK_DATA;
outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT);
dev_dbg(I801_dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
/* Make sure the SMBus host is ready to start transmitting */
temp = inb_p(SMBHSTSTS);
if (i == 1) {
/* Erronenous conditions before transaction:
* Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */
errmask=0x9f;
} else {
/* Erronenous conditions during transaction:
* Failed, Bus_Err, Dev_Err, Intr */
errmask=0x1e;
}
if (temp & errmask) {
dev_dbg(I801_dev, "SMBus busy (%02x). "
"Resetting...\n", temp);
outb_p(temp, SMBHSTSTS);
if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
dev_err(I801_dev,
"Reset failed! (%02x)\n", temp);
result = -1;
goto END;
}
if (i != 1) {
/* if die in middle of block transaction, fail */
result = -1;
goto END;
}
}
if (i == 1)
outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT);
/* We will always wait for a fraction of a second! */
timeout = 0;
do {
i2c_delay(1);
temp = inb_p(SMBHSTSTS);
}
while ((!(temp & 0x80))
&& (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
result = -1;
dev_dbg(I801_dev, "SMBus Timeout!\n");
}
if (temp & 0x10) {
result = -1;
dev_dbg(I801_dev,
"Error: Failed bus transaction\n");
} else if (temp & 0x08) {
result = -1;
dev_err(I801_dev, "Bus collision!\n");
} else if (temp & 0x04) {
result = -1;
dev_dbg(I801_dev, "Error: no response!\n");
}
if (i == 1 && read_write == I2C_SMBUS_READ) {
if (command != I2C_SMBUS_I2C_BLOCK_DATA) {
len = inb_p(SMBHSTDAT0);
if (len < 1)
len = 1;
if (len > 32)
len = 32;
data->block[0] = len;
} else {
/* if slave returns < 32 bytes transaction will fail */
data->block[0] = 32;
}
}
/* Retrieve/store value in SMBBLKDAT */
if (read_write == I2C_SMBUS_READ)
data->block[i] = inb_p(SMBBLKDAT);
if (read_write == I2C_SMBUS_WRITE && i+1 <= len)
outb_p(data->block[i+1], SMBBLKDAT);
if ((temp & 0x9e) != 0x00)
outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */
if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(I801_dev,
"Bad status (%02x) at end of transaction\n",
temp);
}
dev_dbg(I801_dev, "Block (post %d): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
if (result < 0)
goto END;
}
if (hwpec) {
/* wait for INTR bit as advised by Intel */
timeout = 0;
do {
i2c_delay(1);
temp = inb_p(SMBHSTSTS);
} while ((!(temp & 0x02))
&& (timeout++ < MAX_TIMEOUT));
if (timeout >= MAX_TIMEOUT) {
dev_dbg(I801_dev, "PEC Timeout!\n");
}
outb_p(temp, SMBHSTSTS);
}
result = 0;
END:
if (command == I2C_SMBUS_I2C_BLOCK_DATA &&
read_write == I2C_SMBUS_WRITE) {
/* restore saved configuration register value */
pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
}
return result;
}
/* Return -1 on error. */
static s32 i801_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data * data)
{
int hwpec = 0;
int block = 0;
int ret, xact = 0;
#ifdef HAVE_PEC
hwpec = isich4 && (flags & I2C_CLIENT_PEC)
&& size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA;
#endif
switch (size) {
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
xact = I801_QUICK;
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
xact = I801_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
xact = I801_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
xact = I801_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
case I2C_SMBUS_I2C_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
block = 1;
break;
case I2C_SMBUS_PROC_CALL:
default:
dev_err(I801_dev, "Unsupported transaction %d\n", size);
return -1;
}
outb_p(hwpec, SMBAUXCTL); /* enable/disable hardware PEC */
if(block)
ret = i801_block_transaction(data, read_write, size, hwpec);
else {
outb_p(xact | ENABLE_INT9, SMBHSTCNT);
ret = i801_transaction();
}
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
time, so we forcibly disable it after every transaction. */
if (hwpec)
outb_p(0, SMBAUXCTL);
if(block)
return ret;
if(ret)
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
return 0;
switch (xact & 0x7f) {
case I801_BYTE: /* Result put in SMBHSTDAT0 */
case I801_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case I801_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
}
return 0;
}
static void i801_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void i801_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static u32 i801_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK
#ifdef HAVE_PEC
| (isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0)
#endif
#if 0
| (isich5 ? I2C_FUNC_SMBUS_READ_I2C_BLOCK
: 0)
#endif
;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = i801_access,
.functionality = i801_func,
};
static struct i2c_adapter i801_adapter = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801,
.algo = &smbus_algorithm,
.inc_use = i801_inc,
.dec_use = i801_dec,
};
static struct pci_device_id i801_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801AA_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801AB_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801BA_2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801CA_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801DB_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82801EB_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_ESB_4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_ESB2_17,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_ICH6_16,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_ICH7_17,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_ICH8_5,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_ICH9_6,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int err;
if ((err = i801_setup(dev)))
return err;
snprintf(i801_adapter.name, 32,
"SMBus I801 adapter at %04x", i801_smba);
return i2c_add_adapter(&i801_adapter);
}
static void __devexit i801_remove(struct pci_dev *dev)
{
i2c_del_adapter(&i801_adapter);
release_region(i801_smba, (isich4 ? 16 : 8));
}
static struct pci_driver i801_driver = {
.name = "i801 smbus",
.id_table = i801_ids,
.probe = i801_probe,
.remove = __devexit_p(i801_remove),
};
static int __init i2c_i801_init(void)
{
printk(KERN_INFO "i2c-i801 version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&i801_driver);
}
static void __exit i2c_i801_exit(void)
{
pci_unregister_driver(&i801_driver);
}
MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
"Philip Edelbrock <phil@netroedge.com>, "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("I801 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_i801_init);
module_exit(i2c_i801_exit);
-327
View File
@@ -1,327 +0,0 @@
/*
i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
Ralph Metzler <rjkm@thp.uni-koeln.de>, and
Mark D. Studebaker <mdsxyz123@yahoo.com>
Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
Simon Vogl
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.
*/
/*
This interfaces to the I810/I815 to provide access to
the DDC Bus and the I2C Bus.
SUPPORTED DEVICES PCI ID
i810AA 7121
i810AB 7123
i810E 7125
i815 1132
i845G 2562
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/param.h> /* for HZ */
#include "version.h"
#include "sensors_compat.h"
MODULE_LICENSE("GPL");
/* Not defined by any Linux 2.4 kernel */
#define PCI_DEVICE_ID_INTEL_82810E_IG 0x7125
#define PCI_DEVICE_ID_INTEL_82815_CGC 0x1132
#define PCI_DEVICE_ID_INTEL_82845G_IG 0x2562
/* GPIO register locations */
#define I810_IOCONTROL_OFFSET 0x5000
#define I810_HVSYNC 0x00 /* not used */
#define I810_GPIOA 0x10
#define I810_GPIOB 0x14
/* bit locations in the registers */
#define SCL_DIR_MASK 0x0001
#define SCL_DIR 0x0002
#define SCL_VAL_MASK 0x0004
#define SCL_VAL_OUT 0x0008
#define SCL_VAL_IN 0x0010
#define SDA_DIR_MASK 0x0100
#define SDA_DIR 0x0200
#define SDA_VAL_MASK 0x0400
#define SDA_VAL_OUT 0x0800
#define SDA_VAL_IN 0x1000
/* initialization states */
#define INIT1 0x1
#define INIT2 0x2
#define INIT3 0x4
/* delays */
#define CYCLE_DELAY 10
#define TIMEOUT (HZ / 2)
static void config_i810(struct pci_dev *dev);
static unsigned long ioaddr;
/* The i810 GPIO registers have individual masks for each bit
so we never have to read before writing. Nice. */
static void bit_i810i2c_setscl(void *data, int val)
{
writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
ioaddr + I810_GPIOB);
readl(ioaddr + I810_GPIOB); /* flush posted write */
}
static void bit_i810i2c_setsda(void *data, int val)
{
writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
ioaddr + I810_GPIOB);
readl(ioaddr + I810_GPIOB); /* flush posted write */
}
/* The GPIO pins are open drain, so the pins could always remain outputs.
However, some chip versions don't latch the inputs unless they
are set as inputs.
We rely on the i2c-algo-bit routines to set the pins high before
reading the input from other chips. Following guidance in the 815
prog. ref. guide, we do a "dummy write" of 0 to the register before
reading which forces the input value to be latched. We presume this
applies to the 810 as well; shouldn't hurt anyway. This is necessary to get
i2c_algo_bit bit_test=1 to pass. */
static int bit_i810i2c_getscl(void *data)
{
writel(SCL_DIR_MASK, ioaddr + I810_GPIOB);
writel(0, ioaddr + I810_GPIOB);
return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN));
}
static int bit_i810i2c_getsda(void *data)
{
writel(SDA_DIR_MASK, ioaddr + I810_GPIOB);
writel(0, ioaddr + I810_GPIOB);
return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN));
}
static void bit_i810ddc_setscl(void *data, int val)
{
writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
ioaddr + I810_GPIOA);
readl(ioaddr + I810_GPIOA); /* flush posted write */
}
static void bit_i810ddc_setsda(void *data, int val)
{
writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
ioaddr + I810_GPIOA);
readl(ioaddr + I810_GPIOA); /* flush posted write */
}
static int bit_i810ddc_getscl(void *data)
{
writel(SCL_DIR_MASK, ioaddr + I810_GPIOA);
writel(0, ioaddr + I810_GPIOA);
return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN));
}
static int bit_i810ddc_getsda(void *data)
{
writel(SDA_DIR_MASK, ioaddr + I810_GPIOA);
writel(0, ioaddr + I810_GPIOA);
return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN));
}
/* Configures the chip */
void config_i810(struct pci_dev *dev)
{
unsigned long cadr;
/* map I810 memory */
cadr = dev->resource[1].start;
cadr += I810_IOCONTROL_OFFSET;
cadr &= PCI_BASE_ADDRESS_MEM_MASK;
ioaddr = (unsigned long)ioremap_nocache(cadr, 0x1000);
if(ioaddr) {
bit_i810i2c_setscl(NULL, 1);
bit_i810i2c_setsda(NULL, 1);
bit_i810ddc_setscl(NULL, 1);
bit_i810ddc_setsda(NULL, 1);
}
}
static void i810_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void i810_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static struct i2c_algo_bit_data i810_i2c_bit_data = {
.setsda = bit_i810i2c_setsda,
.setscl = bit_i810i2c_setscl,
.getsda = bit_i810i2c_getsda,
.getscl = bit_i810i2c_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT,
};
static struct i2c_adapter i810_i2c_adapter = {
.name = "I810/I815 I2C Adapter",
.id = I2C_HW_B_I810,
.algo_data = &i810_i2c_bit_data,
.inc_use = i810_inc,
.dec_use = i810_dec,
};
static struct i2c_algo_bit_data i810_ddc_bit_data = {
.setsda = bit_i810ddc_setsda,
.setscl = bit_i810ddc_setscl,
.getsda = bit_i810ddc_getsda,
.getscl = bit_i810ddc_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT,
};
static struct i2c_adapter i810_ddc_adapter = {
.name = "I810/I815 DDC Adapter",
.id = I2C_HW_B_I810,
.algo_data = &i810_ddc_bit_data,
.inc_use = i810_inc,
.dec_use = i810_dec,
};
static struct pci_device_id i810_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82810_IG1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82810_IG3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82810E_IG,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82815_CGC,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82845G_IG,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
config_i810(dev);
printk("i2c-i810.o: i810/i815 found.\n");
retval = i2c_bit_add_bus(&i810_i2c_adapter);
if(retval)
return retval;
retval = i2c_bit_add_bus(&i810_ddc_adapter);
if(retval)
i2c_bit_del_bus(&i810_i2c_adapter);
return retval;
}
static void __devexit i810_remove(struct pci_dev *dev)
{
i2c_bit_del_bus(&i810_ddc_adapter);
i2c_bit_del_bus(&i810_i2c_adapter);
}
/* Don't register driver to avoid driver conflicts */
/*
static struct pci_driver i810_driver = {
.name = "i810 smbus",
.id_table = i810_ids,
.probe = i810_probe,
.remove = __devexit_p(i810_remove),
};
*/
static int __init i2c_i810_init(void)
{
struct pci_dev *dev;
const struct pci_device_id *id;
printk("i2c-i810.o version %s (%s)\n", LM_VERSION, LM_DATE);
/*
return pci_module_init(&i810_driver);
*/
pci_for_each_dev(dev) {
id = pci_match_device(i810_ids, dev);
if(id)
if(i810_probe(dev, id) >= 0)
return 0;
}
return -ENODEV;
}
static void __exit i2c_i810_exit(void)
{
/*
pci_unregister_driver(&i810_driver);
*/
i810_remove(NULL);
iounmap((void *)ioaddr);
}
MODULE_AUTHOR
("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Ralph Metzler <rjkm@thp.uni-koeln.de>, and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("I810/I815 I2C/DDC driver");
module_init(i2c_i810_init);
module_exit(i2c_i810_exit);
-380
View File
@@ -1,380 +0,0 @@
/*
i2c-ipmb.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
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.
*/
/*
THIS DOESN'T WORK YET - DON'T BOTHER TRYING IT.
This implements an i2c adapter for the BMC IPMB.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/ipmi.h>
#include "version.h"
static u32 i2c_ipmb_func(struct i2c_adapter *adapter);
int ipmb_access(struct i2c_adapter *adap,struct i2c_msg msgs[],
int num);
/* I2C Data */
static struct i2c_algorithm i2c_ipmb_algorithm = {
.name = "IPMB algorithm",
.id = I2C_ALGO_IPMB,
.master_xfer = ipmb_access,
.functionality = i2c_ipmb_func,
};
#define MAX_IPMB_ADAPTERS 8
static struct i2c_adapter i2c_ipmb_adapter[MAX_IPMB_ADAPTERS];
/* IPMI Data */
#define IPMI_IPMB_CHANNEL 0
static ipmi_user_t i2c_ipmb_user;
static unsigned char ipmi_version_major;
static unsigned char ipmi_version_minor;
static const char msgdata[IPMI_MAX_ADDR_SIZE]; /* ?? */
static struct ipmi_system_interface_addr address = {
IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
IPMI_BMC_CHANNEL,
0
}; /* send address */
static struct ipmi_ipmb_addr ipmb_address = {
IPMI_IPMB_ADDR_TYPE,
IPMI_IPMB_CHANNEL,
0,
0
}; /* send address */
static long msgid; /* message ID */
static int interfaces; /* number of BMC's found */
static struct ipmi_msg tx_message; /* send message */
static unsigned char tx_msg_data[IPMI_MAX_MSG_LENGTH + 50];
static unsigned char rx_msg_data[IPMI_MAX_MSG_LENGTH + 50]; /* sloppy */
/* IPMI Message defs */
/* Network Function Codes */
#define IPMI_NETFN_APP 0x06
/* Commands */
#define IPMI_ENABLE_CHANNEL 0x32
#define IPMI_SEND_MSG 0x34
#define IPMI_GET_CHANNEL_INFO 0x42 /* unfortunately, IPMI 1.5 only */
#define IPMI_MASTER_WR 0x52
/************** Message Sending **************/
static void ipmb_i2c_send_message(struct ipmi_addr *address,
int id, struct ipmi_msg * msg)
{
int err;
#ifdef IPMI_RESPONSE_RESPONSE_TYPE
err = ipmi_request(i2c_ipmb_user, address, id, msg, NULL, 0);
#else
err = ipmi_request(i2c_ipmb_user, address, id, msg, 0);
#endif
if (err)
printk(KERN_INFO "i2c-ipmb.o: ipmi_request error %d\n",
err);
}
/* not used */
static void ipmb_i2c_bmc_send_message(int id, struct ipmi_msg * msg)
{
address.channel = IPMI_BMC_CHANNEL;
ipmb_i2c_send_message((struct ipmi_addr *) &address, id, msg);
}
/* this is for sending commands like master w/r */
static void ipmb_i2c_ipmb_send_message(int id, struct ipmi_msg * msg)
{
/*
address.channel = IPMI_IPMB_CHANNEL;
*/
address.channel = IPMI_BMC_CHANNEL;
ipmb_i2c_send_message((struct ipmi_addr *) &address, id, msg);
}
#if 0
/* not used */
/* this is for an smi message, not for things like master w/r */
static void ipmb_i2c_ipmb_send_smi(int addr, int id, struct ipmi_msg * msg)
{
ipmb_address.slave_addr = addr;
ipmb_address.lun = 0; /* pass in */
ipmb_i2c_send_message((struct ipmi_addr *) &ipmb_address, id, msg);
}
/* not used */
/* Compose and send a "Enable Channel Receive" message */
static void ipmb_enable_channel_rcv(int channel, int code)
{
tx_message.netfn = IPMI_NETFN_APP;
tx_message.cmd = IPMI_ENABLE_CHANNEL;
tx_message.data_len = 2;
tx_message.data = tx_msg_data;
tx_msg_data[0] = channel;
tx_msg_data[1] = code;
ipmb_i2c_bmc_send_message(msgid++, &tx_message);
}
/* not used */
/* Compose and send a "Get Channel Info" message (1.5 only) */
static void ipmb_get_channel_info(int channel)
{
tx_message.netfn = IPMI_NETFN_APP;
tx_message.cmd = IPMI_GET_CHANNEL_INFO;
tx_message.data_len = 1;
tx_message.data = tx_msg_data;
tx_msg_data[0] = channel;
ipmb_i2c_bmc_send_message(msgid++, &tx_message);
}
#endif /* 0 */
/* Compose and send a "Master W/R" message */
static void ipmb_master_wr(int bus, u8 addr, u8 rdcount,
u8 wrcount, u8 *wrdata)
{
printk(KERN_INFO "i2c-ipmb.o: trying bus %d ...\n", bus);
tx_message.netfn = IPMI_NETFN_APP;
tx_message.cmd = IPMI_MASTER_WR;
tx_message.data_len = 3 + wrcount;
tx_msg_data[0] = bus & 0x0f;
tx_msg_data[1] = addr << 1;
tx_msg_data[2] = rdcount;
if(wrcount > 0)
memcpy(tx_msg_data + 3, wrdata, wrcount);
tx_message.data = tx_msg_data;
ipmb_i2c_ipmb_send_message(msgid++, &tx_message);
}
int xchan;
int xbus;
/* look for channels. IPMI 1.5 defines multiple channels and an
easy way to get the information.
We don't bother using SDR type 14 for IPMI 1.0; that isn't
always present anyway. We could try "enable message channel receive"
with a channel state = 2 (query) but 1.0 is likely to have only
IPMB busses anyway. Therefore, we assume there is only
the IPMB at channel 0.
*/
static void ipmb_get_all_channel_info(void)
{
#if 0
if(ipmi_version_major > 1 ||
(ipmi_version_major == 1 && ipmi_version_minor >= 5))
ipmb_get_channel_info(0);
#endif
/*
else
scan SDR's for type 14
else {
for all xchan 0-15
ipmb_enable_channel_rcv(xchan, 2);
*/
xbus = 0;
ipmb_master_wr(xbus, 0x2d, 1, 1, "\0");
/*
*/
/*
else
assume IPMB at channel 0 only
*/
}
/************** Message Receiving **************/
/* not used */
static void ipmb_rcv_channel_info(struct ipmi_msg *msg)
{
u8 channel, type, protocol;
channel = msg->data[1] & 0x0f;
type = msg->data[2] & 0x7f;
protocol = msg->data[3] & 0x1f;
printk(KERN_INFO "i2c-ipmb.o: Channel %d; type 0x%x; protocol 0x%x\n",
channel, type, protocol);
}
/*
return i2c_add_adapter(&i2c_ipmb_adapter);
if (error) {
printk(KERN_ERR "i2c-ipmb.o: Adapter registration failed, "
"module i2c-ipmb.o is not inserted\n.");
return;
}
*/
/*
if(channel < 7)
ipmb_get_channel_info(channel + 1);
*/
static void ipmb_rcv_master_resp(struct ipmi_msg *msg)
{
if(++xbus > 0x0f)
return;
ipmb_master_wr(xbus, 0x2d, 1, 1, "\0");
}
#if 0
/* not used */
static void ipmb_rcv_channel_enable(struct ipmi_msg *msg)
{
int state;
state = msg->data[2] & 1;
printk(KERN_INFO "i2c-ipmb.o: Channel %d; state %d\n",
xchan, state);
if(++xchan > 0x0f)
return;
ipmb_enable_channel_rcv(xchan, 2);
}
#endif /* 0 */
static void ipmb_i2c_msg_handler(struct ipmi_recv_msg *msg,
void *handler_data)
{
int rcvid = msg->msgid & 0xffffff;
int client = (msg->msgid >> 24) & 0xf;
if (msg->msg.data[0] != 0)
printk(KERN_WARNING "i2c-ipmb.o: Error 0x%x on cmd 0x%x/0x%x\n",
msg->msg.data[0], msg->msg.netfn & 0xfe, msg->msg.cmd);
/*
else
*/
ipmb_rcv_master_resp(&(msg->msg));
ipmi_free_recv_msg(msg);
}
static struct ipmi_user_hndl ipmb_hndlrs =
{
.ipmi_recv_hndl = ipmb_i2c_msg_handler,
};
/*************** I2C funtions *******************/
/* Return -1 on error. */
int ipmb_access(struct i2c_adapter *adap,struct i2c_msg msgs[],
int num)
{
}
static u32 i2c_ipmb_func(struct i2c_adapter *adapter)
{
return 0; /* fixme */
}
/**************** Initialization ****************/
/* callback for each BMC found */
static void ipmb_register(int ipmi_intf)
{
unsigned long flags;
int rv;
if(interfaces > 0) { /* 1 max for now */
printk(KERN_INFO
"i2c-ipmb.o: Additional IPMI interface %d not supported\n",
ipmi_intf);
return;
}
rv = ipmi_create_user(ipmi_intf, &ipmb_hndlrs, NULL, &i2c_ipmb_user);
if (rv < 0) {
printk(KERN_ERR "i2c-ipmb.o: Unable to register with ipmi\n");
return;
}
ipmi_get_version(i2c_ipmb_user, &ipmi_version_major,
&ipmi_version_minor);
printk(KERN_INFO
"i2c-ipmb.o: Registered IPMI interface %d with version %d.%d\n",
ipmi_intf, ipmi_version_major, ipmi_version_minor);
interfaces++;
ipmb_get_all_channel_info();
}
static void ipmb_new_smi(int if_num)
{
ipmb_register(if_num);
}
static void ipmb_smi_gone(int if_num)
{
if (interfaces >= 1) {
/*
i2c_del_adapter(&i2c_ipmb_adapter);
*/
ipmi_destroy_user(i2c_ipmb_user);
interfaces--;
}
}
static struct ipmi_smi_watcher smi_watcher =
{
.new_smi = ipmb_new_smi,
.smi_gone = ipmb_smi_gone
};
static int __init i2c_ipmb_init(void)
{
int rv;
printk(KERN_INFO "i2c-ipmb.o version %s (%s)\n", LM_VERSION, LM_DATE);
rv = ipmi_smi_watcher_register(&smi_watcher);
if (rv) {
printk(KERN_WARNING
"ipmi_watchdog: can't register smi watcher\n");
return rv;
}
printk(KERN_INFO "i2c-ipmb.o: BMC access for i2c modules initialized.\n");
return 0;
}
static void __exit i2c_ipmi_exit(void)
{
ipmi_smi_watcher_unregister(&smi_watcher);
ipmb_smi_gone(0);
}
MODULE_AUTHOR("M. D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("IPMB-BMC access through i2c");
MODULE_LICENSE("GPL");
module_init(i2c_ipmb_init);
module_exit(i2c_ipmi_exit);
-251
View File
@@ -1,251 +0,0 @@
/*
i2c-ipmi.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
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.
*/
/*
This implements a "dummy" i2c adapter for clients to access the
BMC via IPMI messages. Supports only one BMC and one client for now!
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/ipmi.h>
#include "version.h"
static u32 i2c_ipmi_func(struct i2c_adapter *adapter);
static int bmcclient_i2c_send_message(struct i2c_adapter *, char *, int);
static void i2c_ipmi_inc_use(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void i2c_ipmi_dec_use(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
/* I2C Data */
static struct i2c_algorithm i2c_ipmi_algorithm = {
.name = "IPMI algorithm",
.id = I2C_ALGO_IPMI,
.slave_send = &bmcclient_i2c_send_message,
.functionality = &i2c_ipmi_func,
};
static struct i2c_adapter i2c_ipmi_adapter = {
.name = "IPMI adapter",
.id = I2C_ALGO_IPMI | I2C_HW_IPMI,
.algo = &i2c_ipmi_algorithm,
.inc_use = &i2c_ipmi_inc_use,
.dec_use = &i2c_ipmi_dec_use,
};
/* IPMI Data */
static ipmi_user_t i2c_ipmi_user;
static unsigned char ipmi_version_major;
static unsigned char ipmi_version_minor;
static const char msgdata[IPMI_MAX_ADDR_SIZE]; /* ?? */
static struct ipmi_addr address = {
IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
IPMI_BMC_CHANNEL,
{0}
}; /* send address */
static int interfaces; /* number of BMC's found */
/* Dummy adapter... */
static u32 i2c_ipmi_func(struct i2c_adapter *adapter)
{
return 0;
}
/************** Message Sending **************/
static int find_client(struct i2c_client * client)
{
int i;
for (i = 0; i < I2C_CLIENT_MAX; i++)
if (client == i2c_ipmi_adapter.clients[i])
return i;
return -1;
}
static void ipmi_i2c_send_message(int id, struct ipmi_msg * msg)
{
#ifdef IPMI_RESPONSE_RESPONSE_TYPE
ipmi_request(i2c_ipmi_user, &address, (long) id, msg, NULL, 0);
#else
ipmi_request(i2c_ipmi_user, &address, (long) id, msg, 0);
#endif
}
/* This is the message send function exported to the client
via the i2c_adapter struct.
We use the existing (but unused) slave_send function pointer.
Hence the ugly casts. */
static int bmcclient_i2c_send_message(struct i2c_adapter *clnt,
char * mesg, int id)
{
struct ipmi_msg *msg = (struct ipmi_msg *) mesg;
struct i2c_client *client = (struct i2c_client *) clnt;
int clientid;
#ifdef DEBUG
if(msg->data == NULL)
printk(KERN_INFO "i2c-ipmi.o: Send 0x%x\n", msg->cmd);
else
printk(KERN_INFO "i2c-ipmi.o: Send 0x%x 0x%x 0x%x\n", msg->cmd, msg->data[0], msg->data[1]);
#endif
/* save the client number in the upper 8 bits of the message id */
if((clientid = find_client(client)) < 0) {
printk(KERN_WARNING "i2c-ipmi.o: Request from unknown client\n");
return -1;
}
id = (id & 0xffffff) | (clientid << 24);
ipmi_i2c_send_message(id, msg);
return 0;
}
/************** Message Receiving **************/
static void ipmi_i2c_msg_handler(struct ipmi_recv_msg *msg,
void *handler_data)
{
int rcvid = msg->msgid & 0xffffff;
int clientid = (msg->msgid >> 24) & 0xff;
#ifdef DEBUG
if (msg->msg.data[0] != 0)
printk(KERN_WARNING "i2c-ipmi.o: Error 0x%x on cmd 0x%x/0x%x\n",
msg->msg.data[0], msg->msg.netfn, msg->msg.cmd);
#endif
/* Protect ourselves here; verify the client and its callback
since the client may have gone away since
the message was sent! */
if(clientid < I2C_CLIENT_MAX &&
i2c_ipmi_adapter.clients[clientid] != NULL &&
i2c_ipmi_adapter.clients[clientid]->driver->command != NULL)
(* i2c_ipmi_adapter.clients[clientid]->driver->command)
(i2c_ipmi_adapter.clients[clientid], rcvid, msg);
else {
printk(KERN_WARNING "i2c-ipmi.o: Response for unknown client\n");
ipmi_free_recv_msg(msg);
}
}
static struct ipmi_user_hndl ipmi_hndlrs =
{
.ipmi_recv_hndl = ipmi_i2c_msg_handler,
};
/**************** Initialization ****************/
/* callback for each BMC found */
static void ipmi_register_bmc(int ipmi_intf)
{
int error;
if(interfaces > 0) { /* 1 max for now */
printk(KERN_INFO
"i2c-ipmi.o: Additional IPMI interface %d not supported\n",
ipmi_intf);
return;
}
error = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &i2c_ipmi_user);
if (error < 0) {
printk(KERN_ERR "i2c-ipmi.o: Unable to register with ipmi\n");
return;
}
error = i2c_add_adapter(&i2c_ipmi_adapter);
if (error) {
printk(KERN_ERR "i2c-ipmi.o: Adapter registration failed, "
"module i2c-ipmi.o is not inserted\n.");
return;
}
ipmi_get_version(i2c_ipmi_user, &ipmi_version_major,
&ipmi_version_minor);
printk(KERN_INFO
"i2c-ipmi.o: Registered IPMI interface %d with version %d.%d\n",
ipmi_intf, ipmi_version_major, ipmi_version_minor);
interfaces++;
}
static void ipmi_new_smi(int if_num)
{
ipmi_register_bmc(if_num);
}
static void ipmi_smi_gone(int if_num)
{
if (interfaces >= 1) {
i2c_del_adapter(&i2c_ipmi_adapter);
ipmi_destroy_user(i2c_ipmi_user);
interfaces--;
}
}
static struct ipmi_smi_watcher smi_watcher =
{
.new_smi = ipmi_new_smi,
.smi_gone = ipmi_smi_gone
};
static int __init i2c_ipmi_init(void)
{
int rv;
printk(KERN_INFO "i2c-ipmi.o version %s (%s)\n", LM_VERSION, LM_DATE);
rv = ipmi_smi_watcher_register(&smi_watcher);
if (rv) {
printk(KERN_WARNING
"ipmi_watchdog: can't register smi watcher\n");
return rv;
}
printk(KERN_INFO "i2c-ipmi.o: BMC access for i2c modules initialized.\n");
return 0;
}
static void __exit i2c_ipmi_exit(void)
{
ipmi_smi_watcher_unregister(&smi_watcher);
ipmi_smi_gone(0);
}
MODULE_AUTHOR("M. D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("IPMI-BMC access through i2c");
MODULE_LICENSE("GPL");
module_init(i2c_ipmi_init);
module_exit(i2c_ipmi_exit);
-88
View File
@@ -1,88 +0,0 @@
/*
i2c-isa.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>
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.
*/
/* This implements an i2c algorithm/adapter for ISA bus. Not that this is
on first sight very useful; almost no functionality is preserved.
Except that it makes writing drivers for chips which can be on both
the SMBus and the ISA bus very much easier. See lm78.c for an example
of this. */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include "version.h"
static u32 isa_func(struct i2c_adapter *adapter);
/* This is the actual algorithm we define */
static struct i2c_algorithm isa_algorithm = {
.name = "ISA bus algorithm",
.id = I2C_ALGO_ISA,
.functionality = isa_func,
};
static void isa_inc_use(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void isa_dec_use(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
/* There can only be one... */
static struct i2c_adapter isa_adapter = {
.name = "ISA main adapter",
.id = I2C_ALGO_ISA | I2C_HW_ISA,
.algo = &isa_algorithm,
.inc_use = isa_inc_use,
.dec_use = isa_dec_use,
};
/* We can't do a thing... */
static u32 isa_func(struct i2c_adapter *adapter)
{
return 0;
}
static int __init i2c_isa_init(void)
{
printk("i2c-isa.o version %s (%s)\n", LM_VERSION, LM_DATE);
return i2c_add_adapter(&isa_adapter);
}
static void __exit i2c_isa_exit(void)
{
i2c_del_adapter(&isa_adapter);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("ISA bus access through i2c");
MODULE_LICENSE("GPL");
module_init(i2c_isa_init);
module_exit(i2c_isa_exit);
-619
View File
@@ -1,619 +0,0 @@
/*
i2c Support for Apple Keywest I2C Bus Controller
Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
Original work by
Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
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.
Changes:
2001/12/13 BenH New implementation
2001/12/15 BenH Add support for "byte" and "quick"
transfers. Add i2c_xfer routine.
My understanding of the various modes supported by keywest are:
- Dumb mode : not implemented, probably direct tweaking of lines
- Standard mode : simple i2c transaction of type
S Addr R/W A Data A Data ... T
- Standard sub mode : combined 8 bit subaddr write with data read
S Addr R/W A SubAddr A Data A Data ... T
- Combined mode : Subaddress and Data sequences appended with no stop
S Addr R/W A SubAddr S Addr R/W A Data A Data ... T
Currently, this driver uses only Standard mode for i2c xfer, and
smbus byte & quick transfers ; and uses StandardSub mode for
other smbus transfers instead of combined as we need that for the
sound driver to be happy
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include "i2c-keywest.h"
#define DBG(x...) do {\
if (debug > 0) \
printk(KERN_DEBUG "KW:" x); \
} while(0)
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
MODULE_LICENSE("GPL");
MODULE_PARM(debug, "i");
int debug = 0;
static struct keywest_iface *ifaces = NULL;
static void
do_stop(struct keywest_iface* iface, int result)
{
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
iface->state = state_stop;
iface->result = result;
}
/* Main state machine for standard & standard sub mode */
static int
handle_interrupt(struct keywest_iface *iface, u8 isr)
{
int ack;
int rearm_timer = 1;
DBG("handle_interrupt(), got: %x, status: %x, state: %d\n",
isr, read_reg(reg_status), iface->state);
if (isr == 0 && iface->state != state_stop) {
do_stop(iface, -1);
return rearm_timer;
}
if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
iface->result = -1;
iface->state = state_stop;
}
switch(iface->state) {
case state_addr:
if (!(isr & KW_I2C_IRQ_ADDR)) {
do_stop(iface, -1);
break;
}
ack = read_reg(reg_status);
DBG("ack on set address: %x\n", ack);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
do_stop(iface, -1);
break;
}
/* Handle rw "quick" mode */
if (iface->datalen == 0)
do_stop(iface, 0);
else if (iface->read_write == I2C_SMBUS_READ) {
iface->state = state_read;
if (iface->datalen > 1)
write_reg(reg_control, read_reg(reg_control)
| KW_I2C_CTL_AAK);
} else {
iface->state = state_write;
DBG("write byte: %x\n", *(iface->data));
write_reg(reg_data, *(iface->data++));
iface->datalen--;
}
break;
case state_read:
if (!(isr & KW_I2C_IRQ_DATA)) {
do_stop(iface, -1);
break;
}
*(iface->data++) = read_reg(reg_data);
DBG("read byte: %x\n", *(iface->data-1));
iface->datalen--;
if (iface->datalen == 0)
iface->state = state_stop;
else
write_reg(reg_control, 0);
break;
case state_write:
if (!(isr & KW_I2C_IRQ_DATA)) {
do_stop(iface, -1);
break;
}
/* Check ack status */
ack = read_reg(reg_status);
DBG("ack on data write: %x\n", ack);
if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
do_stop(iface, -1);
break;
}
if (iface->datalen) {
DBG("write byte: %x\n", *(iface->data));
write_reg(reg_data, *(iface->data++));
iface->datalen--;
} else
do_stop(iface, 0);
break;
case state_stop:
if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
do_stop(iface, -1);
else {
rearm_timer = 0;
iface->state = state_idle;
write_reg(reg_control, 0x00);
write_reg(reg_ier, 0x00);
complete(&iface->complete);
}
break;
}
write_reg(reg_isr, isr);
return rearm_timer;
}
/* Interrupt handler */
static void
keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct keywest_iface *iface = (struct keywest_iface *)dev_id;
spin_lock(&iface->lock);
del_timer(&iface->timeout_timer);
if (handle_interrupt(iface, read_reg(reg_isr))) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
}
spin_unlock(&iface->lock);
}
static void
keywest_timeout(unsigned long data)
{
struct keywest_iface *iface = (struct keywest_iface *)data;
DBG("timeout !\n");
spin_lock_irq(&iface->lock);
if (iface->state != state_idle) {
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
}
spin_unlock(&iface->lock);
}
/*
* SMBUS-type transfer entrypoint
*/
static s32
keywest_smbus_xfer( struct i2c_adapter* adap,
u16 addr,
unsigned short flags,
char read_write,
u8 command,
int size,
union i2c_smbus_data* data)
{
struct keywest_chan* chan = (struct keywest_chan*)adap->data;
struct keywest_iface* iface = chan->iface;
int len;
u8* buffer;
u16 cur_word;
int rc = 0;
if (iface->state == state_dead)
return -1;
/* Prepare datas & select mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
switch (size) {
case I2C_SMBUS_QUICK:
len = 0;
buffer = NULL;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
break;
case I2C_SMBUS_BYTE_DATA:
len = 1;
buffer = &data->byte;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
//iface->cur_mode |= KW_I2C_MODE_COMBINED;
break;
case I2C_SMBUS_WORD_DATA:
len = 2;
cur_word = cpu_to_le16(data->word);
buffer = (u8 *)&cur_word;
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
//iface->cur_mode |= KW_I2C_MODE_COMBINED;
break;
case I2C_SMBUS_BLOCK_DATA:
len = data->block[0];
buffer = &data->block[1];
iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
//iface->cur_mode |= KW_I2C_MODE_COMBINED;
break;
default:
return -1;
}
/* Original driver had this limitation */
if (len > 32)
len = 32;
down(&iface->sem);
DBG("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
iface->data = buffer;
iface->datalen = len;
iface->state = state_addr;
iface->result = 0;
iface->stopretry = 0;
iface->read_write = read_write;
/* Setup channel & clear pending irqs */
write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_isr, read_reg(reg_isr));
write_reg(reg_status, 0);
/* Set up address and r/w bit */
write_reg(reg_addr,
(addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
/* Set up the sub address */
if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
|| (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
write_reg(reg_subaddr, command);
/* Arm timeout */
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
/* Start sending address & enable interrupt*/
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
wait_for_completion(&iface->complete);
rc = iface->result;
DBG("transfer done, result: %d\n", rc);
if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ)
data->word = le16_to_cpu(cur_word);
/* Release sem */
up(&iface->sem);
return rc;
}
/*
* Generic i2c master transfer entrypoint
*/
static int
keywest_xfer( struct i2c_adapter *adap,
struct i2c_msg msgs[],
int num)
{
struct keywest_chan* chan = (struct keywest_chan*)adap->data;
struct keywest_iface* iface = chan->iface;
struct i2c_msg *pmsg;
int i, completed;
int rc = 0;
down(&iface->sem);
/* Set adapter to standard mode */
iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
iface->cur_mode |= KW_I2C_MODE_STANDARD;
completed = 0;
for (i = 0; rc >= 0 && i < num;) {
u8 addr;
pmsg = &msgs[i++];
addr = pmsg->addr;
if (pmsg->flags & I2C_M_TEN) {
printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n");
rc = -EINVAL;
break;
}
DBG("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n",
chan->chan_no,
pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->len, addr, i, num);
/* Setup channel & clear pending irqs */
write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
write_reg(reg_isr, read_reg(reg_isr));
write_reg(reg_status, 0);
iface->data = pmsg->buf;
iface->datalen = pmsg->len;
iface->state = state_addr;
iface->result = 0;
iface->stopretry = 0;
if (pmsg->flags & I2C_M_RD)
iface->read_write = I2C_SMBUS_READ;
else
iface->read_write = I2C_SMBUS_WRITE;
/* Set up address and r/w bit */
if (pmsg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
write_reg(reg_addr,
(addr << 1) |
((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
/* Arm timeout */
iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
add_timer(&iface->timeout_timer);
/* Start sending address & enable interrupt*/
write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
write_reg(reg_ier, KW_I2C_IRQ_MASK);
wait_for_completion(&iface->complete);
rc = iface->result;
if (rc == 0)
completed++;
DBG("transfer done, result: %d\n", rc);
}
/* Release sem */
up(&iface->sem);
return completed;
}
static u32
keywest_func(struct i2c_adapter * adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static void
keywest_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void
keywest_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
/* For now, we only handle combined mode (smbus) */
static struct i2c_algorithm keywest_algorithm = {
.name = "Keywest i2c",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = keywest_smbus_xfer,
.master_xfer = keywest_xfer,
.functionality = keywest_func,
};
static int __init
create_iface(struct device_node* np)
{
unsigned long steps, *psteps, *prate;
unsigned bsteps, tsize, i, nchan, addroffset;
struct keywest_iface* iface;
int error;
psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
steps = psteps ? (*psteps) : 0x10;
/* Hrm... maybe we can be smarter here */
for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
steps >>= 1;
if (!strcmp(np->parent->name, "uni-n")) {
nchan = 2;
addroffset = 3;
} else {
addroffset = 0;
nchan = 1;
}
tsize = sizeof(struct keywest_iface) +
(sizeof(struct keywest_chan) + 4) * nchan;
iface = kmalloc(tsize, GFP_KERNEL);
if (iface == NULL) {
printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
return -ENOMEM;
}
memset(iface, 0, tsize);
init_MUTEX(&iface->sem);
spin_lock_init(&iface->lock);
init_completion(&iface->complete);
iface->bsteps = bsteps;
iface->chan_count = nchan;
iface->state = state_idle;
iface->irq = np->intrs[0].line;
iface->channels = (struct keywest_chan *)
(((unsigned long)(iface + 1) + 3UL) & ~3UL);
iface->base = (unsigned long)ioremap(np->addrs[0].address + addroffset,
np->addrs[0].size);
if (iface->base == 0) {
printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
kfree(iface);
return -ENOMEM;
}
init_timer(&iface->timeout_timer);
iface->timeout_timer.function = keywest_timeout;
iface->timeout_timer.data = (unsigned long)iface;
/* Select interface rate */
iface->cur_mode = KW_I2C_MODE_100KHZ;
prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
if (prate) switch(*prate) {
case 100:
iface->cur_mode = KW_I2C_MODE_100KHZ;
break;
case 50:
iface->cur_mode = KW_I2C_MODE_50KHZ;
break;
case 25:
iface->cur_mode = KW_I2C_MODE_25KHZ;
break;
default:
printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
*prate);
}
/* Select standard mode by default */
iface->cur_mode |= KW_I2C_MODE_STANDARD;
/* Write mode */
write_reg(reg_mode, iface->cur_mode);
/* Switch interrupts off & clear them*/
write_reg(reg_ier, 0x00);
write_reg(reg_isr, KW_I2C_IRQ_MASK);
/* Request chip interrupt */
error = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
if (error) {
printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
iounmap((void *)iface->base);
kfree(iface);
return -ENODEV;
}
for (i=0; i<nchan; i++) {
struct keywest_chan* chan = &iface->channels[i];
sprintf(chan->adapter.name, "%s %d", np->parent->name, i);
chan->iface = iface;
chan->chan_no = i;
chan->adapter.id = I2C_ALGO_SMBUS;
chan->adapter.algo = &keywest_algorithm;
chan->adapter.algo_data = NULL;
chan->adapter.inc_use = keywest_inc;
chan->adapter.dec_use = keywest_dec;
chan->adapter.client_register = NULL;
chan->adapter.client_unregister = NULL;
chan->adapter.data = chan;
error = i2c_add_adapter(&chan->adapter);
if (error) {
printk("i2c-keywest.c: Adapter %s registration failed\n",
chan->adapter.name);
chan->adapter.data = NULL;
}
}
printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
iface->next = ifaces;
ifaces = iface;
return 0;
}
static void __exit
dispose_iface(struct keywest_iface *iface)
{
int i, error;
ifaces = iface->next;
/* Make sure we stop all activity */
down(&iface->sem);
spin_lock_irq(&iface->lock);
while (iface->state != state_idle) {
spin_unlock_irq(&iface->lock);
set_task_state(current,TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10);
spin_lock_irq(&iface->lock);
}
iface->state = state_dead;
spin_unlock_irq(&iface->lock);
free_irq(iface->irq, iface);
up(&iface->sem);
/* Release all channels */
for (i=0; i<iface->chan_count; i++) {
struct keywest_chan* chan = &iface->channels[i];
if (!chan->adapter.data)
continue;
i2c_del_adapter(&chan->adapter);
chan->adapter.data = NULL;
/* We aren't that prepared to deal with this... */
if (error)
printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
}
iounmap((void *)iface->base);
kfree(iface);
}
static int __init i2c_keywest_init(void)
{
struct device_node *np;
int error = -ENODEV;
np = find_compatible_devices("i2c", "keywest");
while (np != 0) {
if (np->n_addrs >= 1 && np->n_intrs >= 1)
error = create_iface(np);
np = np->next;
}
if (ifaces)
error = 0;
return error;
}
static void __exit i2c_keywest_exit(void)
{
while(ifaces)
dispose_iface(ifaces);
}
module_init(i2c_keywest_init);
module_exit(i2c_keywest_exit);
-113
View File
@@ -1,113 +0,0 @@
#ifndef __I2C_KEYWEST_H__
#define __I2C_KEYWEST_H__
#include <asm/semaphore.h>
/* The Tumbler audio equalizer can be really slow sometimes */
#define POLL_TIMEOUT (2*HZ)
/* Register indices */
typedef enum {
reg_mode = 0,
reg_control,
reg_status,
reg_isr,
reg_ier,
reg_addr,
reg_subaddr,
reg_data
} reg_t;
/* Mode register */
#define KW_I2C_MODE_100KHZ 0x00
#define KW_I2C_MODE_50KHZ 0x01
#define KW_I2C_MODE_25KHZ 0x02
#define KW_I2C_MODE_DUMB 0x00
#define KW_I2C_MODE_STANDARD 0x04
#define KW_I2C_MODE_STANDARDSUB 0x08
#define KW_I2C_MODE_COMBINED 0x0C
#define KW_I2C_MODE_MODE_MASK 0x0C
#define KW_I2C_MODE_CHAN_MASK 0xF0
/* Control register */
#define KW_I2C_CTL_AAK 0x01
#define KW_I2C_CTL_XADDR 0x02
#define KW_I2C_CTL_STOP 0x04
#define KW_I2C_CTL_START 0x08
/* Status register */
#define KW_I2C_STAT_BUSY 0x01
#define KW_I2C_STAT_LAST_AAK 0x02
#define KW_I2C_STAT_LAST_RW 0x04
#define KW_I2C_STAT_SDA 0x08
#define KW_I2C_STAT_SCL 0x10
/* IER & ISR registers */
#define KW_I2C_IRQ_DATA 0x01
#define KW_I2C_IRQ_ADDR 0x02
#define KW_I2C_IRQ_STOP 0x04
#define KW_I2C_IRQ_START 0x08
#define KW_I2C_IRQ_MASK 0x0F
/* Physical interface */
struct keywest_iface
{
unsigned long base;
unsigned bsteps;
int irq;
struct semaphore sem;
spinlock_t lock;
struct keywest_chan* channels;
unsigned chan_count;
u8 cur_mode;
char read_write;
u8* data;
unsigned datalen;
int state;
int result;
int stopretry;
struct timer_list timeout_timer;
struct completion complete;
struct keywest_iface* next;
};
enum {
state_idle,
state_addr,
state_read,
state_write,
state_stop,
state_dead
};
/* Channel on an interface */
struct keywest_chan
{
struct i2c_adapter adapter;
struct keywest_iface* iface;
unsigned chan_no;
};
/* Register access */
static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg)
{
return in_8(((volatile u8 *)iface->base)
+ (((unsigned)reg) << iface->bsteps));
}
static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
{
out_8(((volatile u8 *)iface->base)
+ (((unsigned)reg) << iface->bsteps), val);
(void)__read_reg(iface, reg);
udelay(10);
}
#define write_reg(reg, val) __write_reg(iface, reg, val)
#define read_reg(reg) __read_reg(iface, reg)
#endif /* __I2C_KEYWEST_H__ */
-430
View File
@@ -1,430 +0,0 @@
/*
SMBus driver for nVidia nForce2 MCP
Copyright (c) 2003 Hans-Frieder Vogt <hfvogt@arcor.de>,
Based on
SMBus 2.0 driver for AMD-8111 IO-Hub
Copyright (c) 2002 Vojtech Pavlik
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.
*/
/*
SUPPORTED DEVICES PCI ID
nForce2 MCP 0064
nForce2 Ultra 400 MCP 0084
nForce3 Pro150 MCP 00D4
nForce3 250Gb MCP 00E4
nForce4 MCP 0052
nForce4 MCP-04 0034
nForce4 MCP51 0264
nForce4 MCP55 0368
This driver supports the 2 SMBuses that are included in the MCP of the
nForce2/3/4 chipsets.
*/
/* Note: we assume there can only be one nForce2, with two SMBus interfaces */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR ("Hans-Frieder Vogt <hfvogt@arcor.de>");
MODULE_DESCRIPTION("nForce2 SMBus driver");
#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS
#define PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS 0x0064
#endif
#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS
#define PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS 0x0084
#endif
#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS
#define PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS 0x00D4
#endif
#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS
#define PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS 0x00E4
#endif
#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS
#define PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS 0x0052
#endif
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS 0x0034
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS 0x0264
#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS 0x0368
struct nforce2_smbus {
struct i2c_adapter adapter;
int base;
int size;
};
/*
* nVidia nForce2 SMBus control register definitions
* (Newer incarnations use standard BARs 4 and 5 instead)
*/
#define NFORCE_PCI_SMB1 0x50
#define NFORCE_PCI_SMB2 0x54
/*
* ACPI 2.0 chapter 13 SMBus 2.0 EC register model
*/
#define NVIDIA_SMB_PRTCL (smbus->base + 0x00) /* protocol, PEC */
#define NVIDIA_SMB_STS (smbus->base + 0x01) /* status */
#define NVIDIA_SMB_ADDR (smbus->base + 0x02) /* address */
#define NVIDIA_SMB_CMD (smbus->base + 0x03) /* command */
#define NVIDIA_SMB_DATA (smbus->base + 0x04) /* 32 data registers */
#define NVIDIA_SMB_BCNT (smbus->base + 0x24) /* number of data bytes */
#define NVIDIA_SMB_ALRM_A (smbus->base + 0x25) /* alarm address */
#define NVIDIA_SMB_ALRM_D (smbus->base + 0x26) /* 2 bytes alarm data */
#define NVIDIA_SMB_STS_DONE 0x80
#define NVIDIA_SMB_STS_ALRM 0x40
#define NVIDIA_SMB_STS_RES 0x20
#define NVIDIA_SMB_STS_STATUS 0x1f
#define NVIDIA_SMB_PRTCL_WRITE 0x00
#define NVIDIA_SMB_PRTCL_READ 0x01
#define NVIDIA_SMB_PRTCL_QUICK 0x02
#define NVIDIA_SMB_PRTCL_BYTE 0x04
#define NVIDIA_SMB_PRTCL_BYTE_DATA 0x06
#define NVIDIA_SMB_PRTCL_WORD_DATA 0x08
#define NVIDIA_SMB_PRTCL_BLOCK_DATA 0x0a
#define NVIDIA_SMB_PRTCL_PROC_CALL 0x0c
#define NVIDIA_SMB_PRTCL_BLOCK_PROC_CALL 0x0d
#define NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA 0x4a
#define NVIDIA_SMB_PRTCL_PEC 0x80
static struct pci_driver nforce2_driver;
static s32 nforce2_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/*
static int nforce2_block_transaction(union i2c_smbus_data *data,
char read_write, int i2c_enable);
*/
static u32 nforce2_func(struct i2c_adapter *adapter);
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = nforce2_access,
.functionality = nforce2_func,
};
/* Return -1 on error. See smbus.h for more information */
s32 nforce2_access(struct i2c_adapter * adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size,
union i2c_smbus_data * data)
{
struct nforce2_smbus *smbus = adap->algo_data;
unsigned char protocol, pec, temp;
unsigned char len = 0; /* to keep the compiler quiet */
int i;
protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ : NVIDIA_SMB_PRTCL_WRITE;
pec = (flags & I2C_CLIENT_PEC) ? NVIDIA_SMB_PRTCL_PEC : 0;
switch (size) {
case I2C_SMBUS_QUICK:
protocol |= NVIDIA_SMB_PRTCL_QUICK;
read_write = I2C_SMBUS_WRITE;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, NVIDIA_SMB_CMD);
protocol |= NVIDIA_SMB_PRTCL_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(command, NVIDIA_SMB_CMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, NVIDIA_SMB_DATA);
protocol |= NVIDIA_SMB_PRTCL_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(command, NVIDIA_SMB_CMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word, NVIDIA_SMB_DATA);
outb_p(data->word >> 8, NVIDIA_SMB_DATA+1);
}
protocol |= NVIDIA_SMB_PRTCL_WORD_DATA | pec;
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(command, NVIDIA_SMB_CMD);
if (read_write == I2C_SMBUS_WRITE) {
len = min_t(u8, data->block[0], 32);
outb_p(len, NVIDIA_SMB_BCNT);
for (i = 0; i < len; i++)
outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
}
protocol |= NVIDIA_SMB_PRTCL_BLOCK_DATA | pec;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
len = min_t(u8, data->block[0], 32);
outb_p(command, NVIDIA_SMB_CMD);
outb_p(len, NVIDIA_SMB_BCNT);
if (read_write == I2C_SMBUS_WRITE)
for (i = 0; i < len; i++)
outb_p(data->block[i + 1], NVIDIA_SMB_DATA+i);
protocol |= NVIDIA_SMB_PRTCL_I2C_BLOCK_DATA;
break;
case I2C_SMBUS_PROC_CALL:
printk(KERN_WARNING "i2c-nforce2.o: I2C_SMBUS_PROC_CALL not supported!\n");
return -1;
case I2C_SMBUS_BLOCK_PROC_CALL:
printk(KERN_WARNING "i2c-nforce2.o: I2C_SMBUS_BLOCK_PROC_CALL not supported!\n");
return -1;
default:
printk(KERN_WARNING "i2c-nforce2.c: Unsupported transaction %d\n", size);
return -1;
}
outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR);
outb_p(protocol, NVIDIA_SMB_PRTCL);
temp = inb_p(NVIDIA_SMB_STS);
if (~temp & NVIDIA_SMB_STS_DONE) {
udelay(500);
temp = inb_p(NVIDIA_SMB_STS);
}
if (~temp & NVIDIA_SMB_STS_DONE) {
i2c_delay(HZ/100);
temp = inb_p(NVIDIA_SMB_STS);
}
if ((~temp & NVIDIA_SMB_STS_DONE) || (temp & NVIDIA_SMB_STS_STATUS)) {
printk(KERN_DEBUG "i2c-nforce2.o: SMBus Timeout! (0x%02x)\n",
temp);
return -1;
}
if (read_write == I2C_SMBUS_WRITE)
return 0;
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
data->byte = inb_p(NVIDIA_SMB_DATA);
break;
case I2C_SMBUS_WORD_DATA:
/* case I2C_SMBUS_PROC_CALL: not supported */
data->word = inb_p(NVIDIA_SMB_DATA) | (inb_p(NVIDIA_SMB_DATA+1) << 8);
break;
case I2C_SMBUS_BLOCK_DATA:
/* case I2C_SMBUS_BLOCK_PROC_CALL: not supported */
len = inb_p(NVIDIA_SMB_BCNT);
len = min_t(u8, len, 32);
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < len; i++)
data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i);
data->block[0] = len;
break;
}
return 0;
}
u32 nforce2_func(struct i2c_adapter *adapter)
{
/* other functionality might be possible, but is not tested */
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA /* |
I2C_FUNC_SMBUS_BLOCK_DATA */;
}
static void nforce2_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void nforce2_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static struct pci_device_id nforce2_ids[] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE4_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0 }
};
static int __devinit nforce2_probe_smb(struct pci_dev *dev, int bar,
int alt_reg, struct nforce2_smbus *smbus, char *name)
{
int error;
smbus->base = pci_resource_start(dev, bar);
if (smbus->base) {
smbus->size = pci_resource_len(dev, bar);
} else {
/* Older incarnations of the device used non-standard BARs */
u16 iobase;
if (pci_read_config_word(dev, alt_reg, &iobase)
!= PCIBIOS_SUCCESSFUL) {
printk(KERN_ERR "i2c-nforce2.o: Error reading PCI "
"config for %s\n", name);
return -1;
}
smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK;
smbus->size = 8;
}
if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) {
printk (KERN_ERR "i2c-nforce2.o: Error requesting region %02x .. %02X for %s\n", smbus->base, smbus->base+smbus->size-1, name);
return -1;
}
sprintf(smbus->adapter.name, "SMBus nForce2 adapter at %04x", smbus->base);
smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_NFORCE2;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
smbus->adapter.inc_use = nforce2_inc;
smbus->adapter.dec_use = nforce2_dec;
error = i2c_add_adapter(&smbus->adapter);
if (error) {
printk(KERN_WARNING "i2c-nforce2.o: Failed to register adapter.\n");
release_region(smbus->base, smbus->size);
return -1;
}
printk(KERN_INFO "i2c-nforce2.o: nForce2 SMBus adapter at %#x\n", smbus->base);
return 0;
}
static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct nforce2_smbus *smbuses;
int res1, res2;
/* we support 2 SMBus adapters */
if (!(smbuses = kmalloc(2*sizeof(struct nforce2_smbus), GFP_KERNEL)))
return -ENOMEM;
memset (smbuses, 0, 2*sizeof(struct nforce2_smbus));
pci_set_drvdata(dev, smbuses);
/* SMBus adapter 1 */
res1 = nforce2_probe_smb(dev, 4, NFORCE_PCI_SMB1, &smbuses[0], "SMB1");
if (res1 < 0) {
printk (KERN_ERR "i2c-nforce2.o: Error probing SMB1.\n");
smbuses[0].base = 0; /* to have a check value */
}
/* SMBus adapter 2 */
res2 = nforce2_probe_smb(dev, 5, NFORCE_PCI_SMB2, &smbuses[1], "SMB2");
if (res2 < 0) {
printk (KERN_ERR "i2c-nforce2.o: Error probing SMB2.\n");
smbuses[1].base = 0; /* to have a check value */
}
if ((res1 < 0) && (res2 < 0)) {
/* we did not find even one of the SMBuses, so we give up */
kfree(smbuses);
return -ENODEV;
}
return 0;
}
static void __devexit nforce2_remove(struct pci_dev *dev)
{
struct nforce2_smbus *smbuses = (void*) pci_get_drvdata(dev);
if (smbuses[0].base) {
i2c_del_adapter(&smbuses[0].adapter);
release_region(smbuses[0].base, smbuses[0].size);
}
if (smbuses[1].base) {
i2c_del_adapter(&smbuses[1].adapter);
release_region(smbuses[1].base, smbuses[1].size);
}
kfree(smbuses);
}
static struct pci_driver nforce2_driver = {
.name = "nForce2 SMBus",
.id_table = nforce2_ids,
.probe = nforce2_probe,
.remove = __devexit_p(nforce2_remove),
};
int __init nforce2_init(void)
{
printk(KERN_INFO "i2c-nforce2.o version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&nforce2_driver);
}
void __exit nforce2_exit(void)
{
pci_unregister_driver(&nforce2_driver);
}
module_init(nforce2_init);
module_exit(nforce2_exit);
-584
View File
@@ -1,584 +0,0 @@
/*
piix4.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com>
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.
*/
/*
Supports:
Intel PIIX4, 440MX
Serverworks OSB4, CSB5, CSB6, HT-1000
ATI IXP200, IXP300, IXP400, SB600
SMSC Victory66
Note: we assume there can only be one device, with one SMBus interface.
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/apm_bios.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
struct sd {
const unsigned short mfr;
const unsigned short dev;
const unsigned char fn;
const char *name;
};
/* PIIX4 SMBus address offsets */
#define SMBHSTSTS (0 + piix4_smba)
#define SMBHSLVSTS (1 + piix4_smba)
#define SMBHSTCNT (2 + piix4_smba)
#define SMBHSTCMD (3 + piix4_smba)
#define SMBHSTADD (4 + piix4_smba)
#define SMBHSTDAT0 (5 + piix4_smba)
#define SMBHSTDAT1 (6 + piix4_smba)
#define SMBBLKDAT (7 + piix4_smba)
#define SMBSLVCNT (8 + piix4_smba)
#define SMBSHDWCMD (9 + piix4_smba)
#define SMBSLVEVT (0xA + piix4_smba)
#define SMBSLVDAT (0xC + piix4_smba)
/* count for request_region */
#define SMBIOSIZE 8
/* PCI Address Constants */
#define SMBBA 0x090
#define SMBHSTCFG 0x0D2
#define SMBSLVC 0x0D3
#define SMBSHDW1 0x0D4
#define SMBSHDW2 0x0D5
#define SMBREV 0x0D6
/* Other settings */
#define MAX_TIMEOUT 500
#define ENABLE_INT9 0
/* PIIX4 constants */
#define PIIX4_QUICK 0x00
#define PIIX4_BYTE 0x04
#define PIIX4_BYTE_DATA 0x08
#define PIIX4_WORD_DATA 0x0C
#define PIIX4_BLOCK_DATA 0x14
/* insmod parameters */
/* If force is set to anything different from 0, we forcibly enable the
PIIX4. DANGEROUS! */
static int force = 0;
MODULE_PARM(force, "i");
MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!");
/* If force_addr is set to anything different from 0, we forcibly enable
the PIIX4 at the given address. VERY DANGEROUS! */
static int force_addr = 0;
MODULE_PARM(force_addr, "i");
MODULE_PARM_DESC(force_addr,
"Forcibly enable the PIIX4 at the given address. "
"EXTREMELY DANGEROUS!");
static int piix4_transaction(void);
static unsigned short piix4_smba = 0;
static struct pci_driver piix4_driver;
#ifdef CONFIG_X86
/*
* Get DMI information.
*/
static int __devinit ibm_dmi_probe(void)
{
extern int is_unsafe_smbus;
return is_unsafe_smbus;
}
#endif
/* Detect whether a PIIX4 can be found, and initialize it, where necessary.
Note the differences between kernels with the old PCI BIOS interface and
newer kernels with the real PCI interface. In compat.h some things are
defined to make the transition easier. */
static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id)
{
unsigned char temp;
/* match up the function */
if (PCI_FUNC(PIIX4_dev->devfn) != id->driver_data)
return -ENODEV;
printk(KERN_INFO "Found %s device\n", PIIX4_dev->name);
#ifdef CONFIG_X86
if(ibm_dmi_probe() && PIIX4_dev->vendor == PCI_VENDOR_ID_INTEL) {
printk(KERN_ERR "i2c-piix4.o: IBM system detected; this module "
"may corrupt your serial eeprom! Refusing to load "
"module!\n");
return -EPERM;
}
#endif
/* Determine the address of the SMBus areas */
if (force_addr) {
piix4_smba = force_addr & 0xfff0;
force = 0;
} else {
pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba);
piix4_smba &= 0xfff0;
if(piix4_smba == 0) {
printk(KERN_ERR "i2c-piix4.o: SMB base address "
"uninitialized - upgrade BIOS or use "
"force_addr=0xaddr\n");
return -ENODEV;
}
}
if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) {
printk(KERN_ERR "i2c-piix4.o: SMB region 0x%x already in "
"use!\n", piix4_smba);
return -ENODEV;
}
pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp);
/* If force_addr is set, we program the new address here. Just to make
sure, we disable the PIIX4 first. */
if (force_addr) {
pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe);
pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba);
pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01);
printk(KERN_INFO "i2c-piix4.o: WARNING: SMBus interface set to "
"new address %04x!\n", piix4_smba);
} else if ((temp & 1) == 0) {
if (force) {
/* This should never need to be done, but has been
* noted that many Dell machines have the SMBus
* interface on the PIIX4 disabled!? NOTE: This assumes
* I/O space and other allocations WERE done by the
* Bios! Don't complain if your hardware does weird
* things after enabling this. :') Check for Bios
* updates before resorting to this.
*/
pci_write_config_byte(PIIX4_dev, SMBHSTCFG,
temp | 1);
printk(KERN_NOTICE "i2c-piix4.o: WARNING: SMBus "
"interface has been FORCEFULLY ENABLED!\n");
} else {
printk(KERN_ERR "i2c-piix4.o: Host SMBus controller "
"not enabled!\n");
release_region(piix4_smba, SMBIOSIZE);
piix4_smba = 0;
return -ENODEV;
}
}
#ifdef DEBUG
if (((temp & 0x0E) == 8) || ((temp & 0x0E) == 2))
printk(KERN_DEBUG "i2c-piix4.o: Using Interrupt 9 for "
"SMBus.\n");
else if ((temp & 0x0E) == 0)
printk(KERN_DEBUG "i2c-piix4.o: Using Interrupt SMI# "
"for SMBus.\n");
else
printk(KERN_ERR "i2c-piix4.o: Illegal Interrupt configuration "
"(or code out of date)!\n");
pci_read_config_byte(PIIX4_dev, SMBREV, &temp);
printk(KERN_DEBUG "i2c-piix4.o: SMBREV = 0x%X\n", temp);
printk(KERN_DEBUG "i2c-piix4.o: SMBA = 0x%X\n", piix4_smba);
#endif /* DEBUG */
return 0;
}
/* Another internally used function */
int piix4_transaction(void)
{
int temp;
int result = 0;
int timeout = 0;
#ifdef DEBUG
printk
(KERN_DEBUG "i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, "
"DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD),
inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
#endif
/* Make sure the SMBus host is ready to start transmitting */
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
#ifdef DEBUG
printk(KERN_DEBUG "i2c-piix4.o: SMBus busy (%02x). Resetting...\n",
temp);
#endif
outb_p(temp, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
#ifdef DEBUG
printk(KERN_ERR "i2c-piix4.o: Failed! (%02x)\n", temp);
#endif
return -1;
} else {
#ifdef DEBUG
printk(KERN_DEBUG "i2c-piix4.o: Successful!\n");
#endif
}
}
/* start the transaction by setting bit 6 */
outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT);
/* We will always wait for a fraction of a second! (See PIIX4 docs errata) */
do {
i2c_delay(1);
temp = inb_p(SMBHSTSTS);
} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
#ifdef DEBUG
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
printk(KERN_ERR "i2c-piix4.o: SMBus Timeout!\n");
result = -1;
}
#endif
if (temp & 0x10) {
result = -1;
#ifdef DEBUG
printk(KERN_ERR "i2c-piix4.o: Error: Failed bus transaction\n");
#endif
}
if (temp & 0x08) {
result = -1;
printk
(KERN_ERR "i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n"
"reset. (sorry!)\n");
/* Clock stops and slave is stuck in mid-transmission */
}
if (temp & 0x04) {
result = -1;
#ifdef DEBUG
printk(KERN_ERR "i2c-piix4.o: Error: no response!\n");
#endif
}
if (inb_p(SMBHSTSTS) != 0x00)
outb_p(inb(SMBHSTSTS), SMBHSTSTS);
#ifdef DEBUG
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
printk
(KERN_ERR "i2c-piix4.o: Failed reset at end of transaction (%02x)\n",
temp);
}
printk
(KERN_DEBUG "i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, "
"DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD),
inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1));
#endif
return result;
}
/* Return -1 on error. */
s32 piix4_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
int i, len;
switch (size) {
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
size = PIIX4_QUICK;
break;
case I2C_SMBUS_BYTE:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
size = PIIX4_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
size = PIIX4_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
size = PIIX4_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0)
len = 0;
if (len > 32)
len = 32;
outb_p(len, SMBHSTDAT0);
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= len; i++)
outb_p(data->block[i], SMBBLKDAT);
}
size = PIIX4_BLOCK_DATA;
break;
default:
printk
(KERN_WARNING "i2c-piix4.o: Unsupported transaction %d\n", size);
return -1;
}
outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
if (piix4_transaction()) /* Error in transaction */
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK))
return 0;
switch (size) {
case PIIX4_BYTE: /* Where is the result put? I assume here it is in
SMBHSTDAT0 but it might just as well be in the
SMBHSTCMD. No clue in the docs */
data->byte = inb_p(SMBHSTDAT0);
break;
case PIIX4_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case PIIX4_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
case PIIX4_BLOCK_DATA:
data->block[0] = inb_p(SMBHSTDAT0);
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= data->block[0]; i++)
data->block[i] = inb_p(SMBBLKDAT);
break;
}
return 0;
}
static void piix4_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void piix4_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
u32 piix4_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = piix4_access,
.functionality = piix4_func,
};
static struct i2c_adapter piix4_adapter = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4,
.algo = &smbus_algorithm,
.inc_use = piix4_inc,
.dec_use = piix4_dec,
};
#define PCI_DEVICE_ID_ATI_IXP200_SMBUS 0x4353
#define PCI_DEVICE_ID_ATI_IXP300_SMBUS 0x4363
#define PCI_DEVICE_ID_ATI_IXP400_SMBUS 0x4372
#define PCI_DEVICE_ID_ATI_IXP600_SMBUS 0x4385
#ifndef PCI_DEVICE_ID_SERVERWORKS_CSB6
#define PCI_DEVICE_ID_SERVERWORKS_CSB6 0x0203
#endif
#define PCI_DEVICE_ID_SERVERWORKS_HT1000SB 0x0205
static struct pci_device_id piix4_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82371AB_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 3
},
{
.vendor = PCI_VENDOR_ID_ATI,
.device = PCI_DEVICE_ID_ATI_IXP200_SMBUS,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_ATI,
.device = PCI_DEVICE_ID_ATI_IXP300_SMBUS,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_ATI,
.device = PCI_DEVICE_ID_ATI_IXP400_SMBUS,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_ATI,
.device = PCI_DEVICE_ID_ATI_IXP600_SMBUS,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_SERVERWORKS,
.device = PCI_DEVICE_ID_SERVERWORKS_OSB4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_SERVERWORKS,
.device = PCI_DEVICE_ID_SERVERWORKS_CSB5,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_SERVERWORKS,
.device = PCI_DEVICE_ID_SERVERWORKS_CSB6,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_SERVERWORKS,
.device = PCI_DEVICE_ID_SERVERWORKS_HT1000SB,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_82443MX_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 3,
},
{
.vendor = PCI_VENDOR_ID_EFAR,
.device = PCI_DEVICE_ID_EFAR_SLC90E66_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = 0,
},
{ 0, }
};
static int __devinit piix4_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
int retval;
retval = piix4_setup(dev, id);
if (retval)
return retval;
sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x",
piix4_smba);
if ((retval = i2c_add_adapter(&piix4_adapter))) {
printk(KERN_ERR "i2c-piix4.o: Couldn't register adapter!\n");
release_region(piix4_smba, SMBIOSIZE);
piix4_smba = 0;
}
return retval;
}
static void __devexit piix4_remove(struct pci_dev *dev)
{
if (piix4_smba) {
i2c_del_adapter(&piix4_adapter);
release_region(piix4_smba, SMBIOSIZE);
piix4_smba = 0;
}
}
static struct pci_driver piix4_driver = {
.name = "piix4 smbus",
.id_table = piix4_ids,
.probe = piix4_probe,
.remove = __devexit_p(piix4_remove),
};
static int __init i2c_piix4_init(void)
{
printk("i2c-piix4.o version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&piix4_driver);
}
static void __exit i2c_piix4_exit(void)
{
pci_unregister_driver(&piix4_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("PIIX4 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_piix4_init);
module_exit(i2c_piix4_exit);
-243
View File
@@ -1,243 +0,0 @@
/*
i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (C) 1998-2003 The LM Sensors Team
Alexander Wold <awold@bigfoot.com>
Mark D. Studebaker <mdsxyz123@yahoo.com>
Based on i2c-voodoo3.c.
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.
*/
/* This interfaces to the I2C bus of the Savage4 to gain access to
the BT869 and possibly other I2C devices. The DDC bus is not
yet supported because its register is not memory-mapped.
However we leave the DDC code here, commented out, to make
it easier to add later.
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/param.h> /* for HZ */
#include "version.h"
#include "sensors_compat.h"
/* 3DFX defines */
/* #define PCI_VENDOR_ID_S3 0x5333 */
#define PCI_CHIP_SAVAGE3D 0x8A20
#define PCI_CHIP_SAVAGE3D_MV 0x8A21
#define PCI_CHIP_SAVAGE4 0x8A22
#define PCI_CHIP_SAVAGE2000 0x9102
#define PCI_CHIP_PROSAVAGE_PM 0x8A25
#define PCI_CHIP_PROSAVAGE_KM 0x8A26
#define PCI_CHIP_SAVAGE_MX_MV 0x8c10
#define PCI_CHIP_SAVAGE_MX 0x8c11
#define PCI_CHIP_SAVAGE_IX_MV 0x8c12
#define PCI_CHIP_SAVAGE_IX 0x8c13
#define REG 0xff20 /* Serial Port 1 Register */
/* bit locations in the register */
//#define DDC_ENAB 0x00040000
//#define DDC_SCL_OUT 0x00080000
//#define DDC_SDA_OUT 0x00100000
//#define DDC_SCL_IN 0x00200000
//#define DDC_SDA_IN 0x00400000
#define I2C_ENAB 0x00000020
#define I2C_SCL_OUT 0x00000001
#define I2C_SDA_OUT 0x00000002
#define I2C_SCL_IN 0x00000008
#define I2C_SDA_IN 0x00000010
/* initialization states */
#define INIT2 0x20
/* #define INIT3 0x4 */
/* delays */
#define CYCLE_DELAY 10
#define TIMEOUT (HZ / 2)
static void config_s4(struct pci_dev *dev);
static unsigned long ioaddr;
/* The sav GPIO registers don't have individual masks for each bit
so we always have to read before writing. */
static void bit_savi2c_setscl(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if(val)
r |= I2C_SCL_OUT;
else
r &= ~I2C_SCL_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
static void bit_savi2c_setsda(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if(val)
r |= I2C_SDA_OUT;
else
r &= ~I2C_SDA_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
/* The GPIO pins are open drain, so the pins always remain outputs.
We rely on the i2c-algo-bit routines to set the pins high before
reading the input from other chips. */
static int bit_savi2c_getscl(void *data)
{
return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
}
static int bit_savi2c_getsda(void *data)
{
return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
}
/* Configures the chip */
void config_s4(struct pci_dev *dev)
{
unsigned int cadr;
/* map memory */
cadr = dev->resource[0].start;
cadr &= PCI_BASE_ADDRESS_MEM_MASK;
ioaddr = (unsigned long)ioremap_nocache(cadr, 0x0080000);
if(ioaddr) {
// writel(0x8160, ioaddr + REG2);
writel(0x00000020, ioaddr + REG);
printk("i2c-savage4: Using Savage4 at 0x%lx\n", ioaddr);
}
}
static void savage4_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void savage4_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static struct i2c_algo_bit_data sav_i2c_bit_data = {
.setsda = bit_savi2c_setsda,
.setscl = bit_savi2c_setscl,
.getsda = bit_savi2c_getsda,
.getscl = bit_savi2c_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
static struct i2c_adapter savage4_i2c_adapter = {
.name = "I2C Savage4 adapter",
.id = I2C_HW_B_SAVG,
.algo_data = &sav_i2c_bit_data,
.inc_use = savage4_inc,
.dec_use = savage4_dec,
};
static struct pci_device_id savage4_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_S3,
.device = PCI_CHIP_SAVAGE4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_S3,
.device = PCI_CHIP_SAVAGE2000,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
config_s4(dev);
return i2c_bit_add_bus(&savage4_i2c_adapter);
}
static void __devexit savage4_remove(struct pci_dev *dev)
{
i2c_bit_del_bus(&savage4_i2c_adapter);
}
/* Don't register driver to avoid driver conflicts */
/*
static struct pci_driver savage4_driver = {
.name = "savage4 smbus",
.id_table = savage4_ids,
.probe = savage4_probe,
.remove = __devexit_p(savage4_remove),
};
*/
static int __init i2c_savage4_init(void)
{
struct pci_dev *dev;
const struct pci_device_id *id;
printk("i2c-savage4.o version %s (%s)\n", LM_VERSION, LM_DATE);
/*
return pci_module_init(&savage4_driver);
*/
pci_for_each_dev(dev) {
id = pci_match_device(savage4_ids, dev);
if(id)
if(savage4_probe(dev, id) >= 0)
return 0;
}
return -ENODEV;
}
static void __exit i2c_savage4_exit(void)
{
/*
pci_unregister_driver(&savage4_driver);
*/
savage4_remove(NULL);
iounmap((void *)ioaddr);
}
MODULE_AUTHOR("Alexander Wold <awold@bigfoot.com> "
"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("Savage4 I2C/SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_savage4_init);
module_exit(i2c_savage4_exit);
-495
View File
@@ -1,495 +0,0 @@
/*
sis5595.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
Philip Edelbrock <phil@netroedge.com>
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.
*/
/* Note: we assume there can only be one SIS5595 with one SMBus interface */
/*
Note: all have mfr. ID 0x1039.
SUPPORTED PCI ID
5595 0008
Note: these chips contain a 0008 device which is incompatible with the
5595. We recognize these by the presence of the listed
"blacklist" PCI ID and refuse to load.
NOT SUPPORTED PCI ID BLACKLIST PCI ID
540 0008 0540
550 0008 0550
5513 0008 5511
5581 0008 5597
5582 0008 5597
5597 0008 5597
5598 0008 5597/5598
630 0008 0630
645 0008 0645
646 0008 0646
648 0008 0648
650 0008 0650
651 0008 0651
730 0008 0730
735 0008 0735
745 0008 0745
746 0008 0746
*/
/* TO DO:
* Add Block Transfers (ugly, but supported by the adapter)
* Add adapter resets
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
MODULE_LICENSE("GPL");
static int blacklist[] = {
PCI_DEVICE_ID_SI_540,
PCI_DEVICE_ID_SI_550,
PCI_DEVICE_ID_SI_630,
PCI_DEVICE_ID_SI_730,
PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but
that ID shows up in other chips so we
use the 5511 ID for recognition */
PCI_DEVICE_ID_SI_5597,
PCI_DEVICE_ID_SI_5598,
0x645,
0x646,
0x648,
0x650,
0x651,
0x735,
0x745,
0x746,
0 };
/* Length of ISA address segment */
#define SIS5595_EXTENT 8
/* SIS5595 SMBus registers */
#define SMB_STS_LO 0x00
#define SMB_STS_HI 0x01
#define SMB_CTL_LO 0x02
#define SMB_CTL_HI 0x03
#define SMB_ADDR 0x04
#define SMB_CMD 0x05
#define SMB_PCNT 0x06
#define SMB_CNT 0x07
#define SMB_BYTE 0x08
#define SMB_DEV 0x10
#define SMB_DB0 0x11
#define SMB_DB1 0x12
#define SMB_HAA 0x13
/* PCI Address Constants */
#define SMB_INDEX 0x38
#define SMB_DAT 0x39
#define SIS5595_ENABLE_REG 0x40
#define ACPI_BASE 0x90
/* Other settings */
#define MAX_TIMEOUT 500
/* SIS5595 constants */
#define SIS5595_QUICK 0x00
#define SIS5595_BYTE 0x02
#define SIS5595_BYTE_DATA 0x04
#define SIS5595_WORD_DATA 0x06
#define SIS5595_PROC_CALL 0x08
#define SIS5595_BLOCK_DATA 0x0A
/* insmod parameters */
/* If force_addr is set to anything different from 0, we forcibly enable
the device at the given address. */
static int force_addr = 0;
MODULE_PARM(force_addr, "i");
MODULE_PARM_DESC(force_addr,
"Initialize the base address of the i2c controller");
static int sis5595_transaction(void);
static struct pci_driver sis5595_driver;
static unsigned short sis5595_base = 0;
static u8 sis5595_read(u8 reg)
{
outb(reg, sis5595_base + SMB_INDEX);
return inb(sis5595_base + SMB_DAT);
}
static void sis5595_write(u8 reg, u8 data)
{
outb(reg, sis5595_base + SMB_INDEX);
outb(data, sis5595_base + SMB_DAT);
}
/* Detect whether a SIS5595 can be found, and initialize it, where necessary.
Note the differences between kernels with the old PCI BIOS interface and
newer kernels with the real PCI interface. In compat.h some things are
defined to make the transition easier. */
int sis5595_setup(struct pci_dev *SIS5595_dev)
{
u16 a;
u8 val;
int *i;
/* Look for imposters */
for(i = blacklist; *i != 0; i++) {
if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) {
printk("i2c-sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i);
return -ENODEV;
}
}
/* Determine the address of the SMBus areas */
pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base);
if(sis5595_base == 0 && force_addr == 0) {
printk("i2c-sis5595.o: ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n");
return -ENODEV;
}
if(force_addr)
sis5595_base = force_addr & ~(SIS5595_EXTENT - 1);
#ifdef DEBUG
printk("ACPI Base address: %04x\n", sis5595_base);
#endif
/* NB: We grab just the two SMBus registers here, but this may still
* interfere with ACPI :-( */
if (check_region(sis5595_base + SMB_INDEX, 2)) {
printk
("i2c-sis5595.o: SMBus registers 0x%04x-0x%04x already in use!\n",
sis5595_base + SMB_INDEX,
sis5595_base + SMB_INDEX + 1);
return -ENODEV;
}
if(force_addr) {
printk("i2c-sis5595.o: forcing ISA address 0x%04X\n", sis5595_base);
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base))
return -ENODEV;
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(SIS5595_dev, ACPI_BASE, &a))
return -ENODEV;
if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) {
/* doesn't work for some chips! */
printk("i2c-sis5595.o: force address failed - not supported?\n");
return -ENODEV;
}
}
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val))
return -ENODEV;
if((val & 0x80) == 0) {
printk("sis5595.o: enabling ACPI\n");
if (PCIBIOS_SUCCESSFUL !=
pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG,
val | 0x80))
return -ENODEV;
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val))
return -ENODEV;
if((val & 0x80) == 0) { /* doesn't work for some chips? */
printk("sis5595.o: ACPI enable failed - not supported?\n");
return -ENODEV;
}
}
/* Everything is happy, let's grab the memory and set things up. */
request_region(sis5595_base + SMB_INDEX, 2, sis5595_driver.name);
return(0);
}
/* Another internally used function */
int sis5595_transaction(void)
{
int temp;
int result = 0;
int timeout = 0;
/* Make sure the SMBus host is ready to start transmitting */
if (
(temp =
sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
0x00) {
#ifdef DEBUG
printk("i2c-sis5595.o: SMBus busy (%04x). Resetting...\n",
temp);
#endif
sis5595_write(SMB_STS_LO, temp & 0xff);
sis5595_write(SMB_STS_HI, temp >> 8);
if (
(temp =
sis5595_read(SMB_STS_LO) +
(sis5595_read(SMB_STS_HI) << 8)) != 0x00) {
#ifdef DEBUG
printk("i2c-sis5595.o: Failed! (%02x)\n", temp);
#endif
return -1;
} else {
#ifdef DEBUG
printk("i2c-sis5595.o: Successful!\n");
#endif
}
}
/* start the transaction by setting bit 4 */
sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10);
/* We will always wait for a fraction of a second! */
do {
i2c_delay(1);
temp = sis5595_read(SMB_STS_LO);
} while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
#ifdef DEBUG
printk("i2c-sis5595.o: SMBus Timeout!\n");
#endif
result = -1;
}
if (temp & 0x10) {
result = -1;
#ifdef DEBUG
printk("i2c-sis5595.o: Error: Failed bus transaction\n");
#endif
}
if (temp & 0x20) {
result = -1;
printk
("i2c-sis5595.o: Bus collision! SMBus may be locked until next hard\n"
"reset (or not...)\n");
/* Clock stops and slave is stuck in mid-transmission */
}
if (
(temp =
sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
0x00) {
sis5595_write(SMB_STS_LO, temp & 0xff);
sis5595_write(SMB_STS_HI, temp >> 8);
}
if (
(temp =
sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) !=
0x00) {
#ifdef DEBUG
printk
("i2c-sis5595.o: Failed reset at end of transaction (%02x)\n",
temp);
#endif
}
return result;
}
/* Return -1 on error. */
s32 sis5595_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
switch (size) {
case I2C_SMBUS_QUICK:
sis5595_write(SMB_ADDR,
((addr & 0x7f) << 1) | (read_write & 0x01));
size = SIS5595_QUICK;
break;
case I2C_SMBUS_BYTE:
sis5595_write(SMB_ADDR,
((addr & 0x7f) << 1) | (read_write & 0x01));
if (read_write == I2C_SMBUS_WRITE)
sis5595_write(SMB_CMD, command);
size = SIS5595_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
sis5595_write(SMB_ADDR,
((addr & 0x7f) << 1) | (read_write & 0x01));
sis5595_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE)
sis5595_write(SMB_BYTE, data->byte);
size = SIS5595_BYTE_DATA;
break;
case I2C_SMBUS_PROC_CALL:
case I2C_SMBUS_WORD_DATA:
sis5595_write(SMB_ADDR,
((addr & 0x7f) << 1) | (read_write & 0x01));
sis5595_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
sis5595_write(SMB_BYTE, data->word & 0xff);
sis5595_write(SMB_BYTE + 1,
(data->word & 0xff00) >> 8);
}
size =
(size ==
I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL :
SIS5595_WORD_DATA;
break;
/*
case I2C_SMBUS_BLOCK_DATA:
printk("sis5595.o: Block data not yet implemented!\n");
return -1;
break;
*/
default:
printk
(KERN_WARNING "sis5595.o: Unsupported transaction %d\n", size);
return -1;
}
sis5595_write(SMB_CTL_LO, ((size & 0x0E)));
if (sis5595_transaction()) /* Error in transaction */
return -1;
if ((size != SIS5595_PROC_CALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK)))
return 0;
switch (size) {
case SIS5595_BYTE: /* Where is the result put? I assume here it is in
SMB_DATA but it might just as well be in the
SMB_CMD. No clue in the docs */
case SIS5595_BYTE_DATA:
data->byte = sis5595_read(SMB_BYTE);
break;
case SIS5595_WORD_DATA:
case SIS5595_PROC_CALL:
data->word =
sis5595_read(SMB_BYTE) +
(sis5595_read(SMB_BYTE + 1) << 8);
break;
}
return 0;
}
static void sis5595_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void sis5595_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
u32 sis5595_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_PROC_CALL;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = sis5595_access,
.functionality = sis5595_func,
};
static struct i2c_adapter sis5595_adapter = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS5595,
.algo = &smbus_algorithm,
.inc_use = sis5595_inc,
.dec_use = sis5595_dec,
};
static struct pci_device_id sis5595_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_SI,
.device = PCI_DEVICE_ID_SI_503,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (sis5595_setup(dev)) {
printk
("i2c-sis5595.o: SIS5595 not detected, module not inserted.\n");
return -ENODEV;
}
sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x",
sis5595_base + SMB_INDEX);
i2c_add_adapter(&sis5595_adapter);
return 0;
}
static void __devexit sis5595_remove(struct pci_dev *dev)
{
i2c_del_adapter(&sis5595_adapter);
release_region(sis5595_base + SMB_INDEX, 2);
}
static struct pci_driver sis5595_driver = {
.name = "sis5595 smbus",
.id_table = sis5595_ids,
.probe = sis5595_probe,
.remove = __devexit_p(sis5595_remove),
};
static int __init i2c_sis5595_init(void)
{
printk("i2c-sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&sis5595_driver);
}
static void __exit i2c_sis5595_exit(void)
{
pci_unregister_driver(&sis5595_driver);
}
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("SIS5595 SMBus driver");
module_init(i2c_sis5595_init);
module_exit(i2c_sis5595_exit);
-544
View File
@@ -1,544 +0,0 @@
/*
i2c-sis630.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2002,2003 Alexander Malysh <amalysh@web.de>
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.
*/
/*
Changes:
24.08.2002
Fixed the typo in sis630_access (Thanks to Mark M. Hoffman)
Changed sis630_transaction.(Thanks to Mark M. Hoffman)
18.09.2002
Added SIS730 as supported.
21.09.2002
Added high_clock module option.If this option is set
used Host Master Clock 56KHz (default 14KHz).For now we save old Host
Master Clock and after transaction completed restore (otherwise
it's confuse BIOS and hung Machine).
24.09.2002
Fixed typo in sis630_access
Fixed logical error by restoring of Host Master Clock
31.07.2003
Added block data read/write support.
*/
/*
Status: beta
Supports:
SIS 630
SIS 730
Note: we assume there can only be one device, with one SMBus interface.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
#ifdef DEBUG
#define DBG(x...) printk(KERN_DEBUG "i2c-sis630.o: " x)
#else
#define DBG(x...)
#endif
/* SIS630 SMBus registers */
#define SMB_STS 0x80 /* status */
#define SMB_EN 0x81 /* status enable */
#define SMB_CNT 0x82
#define SMBHOST_CNT 0x83
#define SMB_ADDR 0x84
#define SMB_CMD 0x85
#define SMB_PCOUNT 0x86 /* processed count */
#define SMB_COUNT 0x87
#define SMB_BYTE 0x88 /* ~0x8F data byte field */
#define SMBDEV_ADDR 0x90
#define SMB_DB0 0x91
#define SMB_DB1 0x92
#define SMB_SAA 0x93
/* register count for request_region */
#define SIS630_SMB_IOREGION 20
/* PCI address constants */
/* acpi base address register */
#define SIS630_ACPI_BASE_REG 0x74
/* bios control register */
#define SIS630_BIOS_CTL_REG 0x40
/* Other settings */
#define MAX_TIMEOUT 500
/* SIS630 constants */
#define SIS630_QUICK 0x00
#define SIS630_BYTE 0x01
#define SIS630_BYTE_DATA 0x02
#define SIS630_WORD_DATA 0x03
#define SIS630_PCALL 0x04
#define SIS630_BLOCK_DATA 0x05
static struct pci_driver sis630_driver;
/* insmod parameters */
static int high_clock = 0;
static int force = 0;
MODULE_PARM(high_clock, "i");
MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz).");
MODULE_PARM(force, "i");
MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!");
/* acpi base address */
static unsigned short acpi_base = 0;
/* supported chips */
static int supported[] = {
PCI_DEVICE_ID_SI_630,
PCI_DEVICE_ID_SI_730,
0 /* terminates the list */
};
static inline u8 sis630_read(u8 reg) {
return inb(acpi_base + reg);
}
static inline void sis630_write(u8 reg, u8 data) {
outb(data, acpi_base + reg);
}
static int sis630_transaction_start(int size, u8 *oldclock) {
int temp;
/*
Make sure the SMBus host is ready to start transmitting.
*/
if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
DBG("SMBus busy (%02x).Resetting...\n",temp);
/* kill smbus transaction */
sis630_write(SMBHOST_CNT, 0x20);
if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
DBG("Failed! (%02x)\n", temp);
return -1;
} else {
DBG("Successful!\n");
}
}
/* save old clock, so we can prevent machine for hung */
*oldclock = sis630_read(SMB_CNT);
DBG("saved clock 0x%02x\n", *oldclock);
/* disable timeout interrupt , set Host Master Clock to 56KHz if requested */
if (high_clock > 0)
sis630_write(SMB_CNT, 0x20);
else
sis630_write(SMB_CNT, (*oldclock & ~0x40));
/* clear all sticky bits */
temp = sis630_read(SMB_STS);
sis630_write(SMB_STS, temp & 0x1e);
/* start the transaction by setting bit 4 and size */
sis630_write(SMBHOST_CNT,0x10 | (size & 0x07));
return 0;
}
static int sis630_transaction_wait(int size) {
int temp, result = 0, timeout = 0;
/* We will always wait for a fraction of a second! */
do {
i2c_delay(1);
temp = sis630_read(SMB_STS);
/* check if block transmitted */
if (size == SIS630_BLOCK_DATA && (temp & 0x10))
break;
} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
DBG("SMBus Timeout!\n");
result = -1;
}
if (temp & 0x02) {
result = -1;
DBG("Error: Failed bus transaction\n");
}
if (temp & 0x04) {
result = -1;
printk(KERN_ERR "i2c-sis630.o: Bus collision!\n");
/*
TBD: Datasheet say:
the software should clear this bit and restart SMBUS operation.
Should we do it or user start request again?
*/
}
return result;
}
static void sis630_transaction_end(u8 oldclock) {
int temp = 0;
/* clear all status "sticky" bits */
sis630_write(SMB_STS, temp);
DBG("SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT));
/*
* restore old Host Master Clock if high_clock is set
* and oldclock was not 56KHz
*/
if (high_clock > 0 && !(oldclock & 0x20))
sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20));
DBG("SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT));
}
static int sis630_transaction(int size) {
int result = 0;
u8 oldclock = 0;
if (!(result = sis630_transaction_start(size, &oldclock))) {
result = sis630_transaction_wait(size);
sis630_transaction_end(oldclock);
}
return result;
}
static int sis630_block_data(union i2c_smbus_data * data, int read_write) {
int i, len = 0, rc = 0;
u8 oldclock = 0;
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
if (len < 0)
len = 0;
else if (len > 32)
len = 32;
sis630_write(SMB_COUNT, len);
for (i=1; i <= len; i++) {
DBG("set data 0x%02x\n", data->block[i]);
/* set data */
sis630_write(SMB_BYTE+(i-1)%8, data->block[i]);
if (i==8 || (len<8 && i==len)) {
DBG("start trans len=%d i=%d\n",len ,i);
/* first transaction */
if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock))
return -1;
}
else if ((i-1)%8 == 7 || i==len) {
DBG("trans_wait len=%d i=%d\n",len,i);
if (i>8) {
DBG("clear smbary_sts len=%d i=%d\n",len,i);
/*
If this is not first transaction,
we must clear sticky bit.
clear SMBARY_STS
*/
sis630_write(SMB_STS,0x10);
}
if (sis630_transaction_wait(SIS630_BLOCK_DATA)) {
DBG("trans_wait failed\n");
rc = -1;
break;
}
}
}
}
else { /* read request */
data->block[0] = len = 0;
if (sis630_transaction_start(SIS630_BLOCK_DATA, &oldclock)) {
return -1;
}
do {
if (sis630_transaction_wait(SIS630_BLOCK_DATA)) {
DBG("trans_wait failed\n");
rc = -1;
break;
}
/* if this first transaction then read byte count */
if (len == 0)
data->block[0] = sis630_read(SMB_COUNT);
/* just to be sure */
if (data->block[0] > 32)
data->block[0] = 32;
DBG("block data read len=0x%x\n", data->block[0]);
for (i=0; i < 8 && len < data->block[0]; i++,len++) {
DBG("read i=%d len=%d\n", i, len);
data->block[len+1] = sis630_read(SMB_BYTE+i);
}
DBG("clear smbary_sts len=%d i=%d\n",len,i);
/* clear SMBARY_STS */
sis630_write(SMB_STS,0x10);
} while(len < data->block[0]);
}
sis630_transaction_end(oldclock);
return rc;
}
/* Return -1 on error. */
static s32 sis630_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
switch (size) {
case I2C_SMBUS_QUICK:
sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
size = SIS630_QUICK;
break;
case I2C_SMBUS_BYTE:
sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
if (read_write == I2C_SMBUS_WRITE)
sis630_write(SMB_CMD, command);
size = SIS630_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
sis630_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE)
sis630_write(SMB_BYTE, data->byte);
size = SIS630_BYTE_DATA;
break;
case I2C_SMBUS_PROC_CALL:
case I2C_SMBUS_WORD_DATA:
sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
sis630_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
sis630_write(SMB_BYTE, data->word & 0xff);
sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8);
}
size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA);
break;
case I2C_SMBUS_BLOCK_DATA:
sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01));
sis630_write(SMB_CMD, command);
size = SIS630_BLOCK_DATA;
return sis630_block_data(data, read_write);
default:
printk("Unsupported I2C size\n");
return -1;
break;
}
if (sis630_transaction(size))
return -1;
if ((size != SIS630_PCALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
return 0;
}
switch(size) {
case SIS630_BYTE:
case SIS630_BYTE_DATA:
data->byte = sis630_read(SMB_BYTE);
break;
case SIS630_PCALL:
case SIS630_WORD_DATA:
data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
break;
default:
return -1;
break;
}
return 0;
}
static void sis630_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void sis630_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static u32 sis630_func(struct i2c_adapter *adapter) {
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
static int __devinit sis630_setup(struct pci_dev *sis630_dev) {
unsigned char b;
struct pci_dev *dummy = NULL;
int i;
/* check for supported SiS devices */
for (i=0; supported[i] > 0; i++) {
if ((dummy = pci_find_device(PCI_VENDOR_ID_SI, supported[i], dummy)))
break; /* found */
}
if (!dummy && force > 0) {
printk(KERN_ERR "i2c-sis630.o: WARNING: Can't detect SIS630 compatible device, but "
"loading because of force option enabled\n");
}
else if (!dummy) {
return -ENODEV;
}
/*
Enable ACPI first , so we can accsess reg 74-75
in acpi io space and read acpi base addr
*/
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) {
printk(KERN_ERR "i2c-sis630.o: Error: Can't read bios ctl reg\n");
return -ENODEV;
}
/* if ACPI already enabled , do nothing */
if (!(b & 0x80) &&
PCIBIOS_SUCCESSFUL !=
pci_write_config_byte(sis630_dev,SIS630_BIOS_CTL_REG,b|0x80)) {
printk(KERN_ERR "i2c-sis630.o: Error: Can't enable ACPI\n");
return -ENODEV;
}
/* Determine the ACPI base address */
if (PCIBIOS_SUCCESSFUL !=
pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) {
printk(KERN_ERR "i2c-sis630.o: Error: Can't determine ACPI base address\n");
return -ENODEV;
}
DBG("ACPI base at 0x%04x\n", acpi_base);
/* Everything is happy, let's grab the memory and set things up. */
if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
sis630_driver.name)){
printk(KERN_ERR "i2c-sis630.o: SMBus registers 0x%04x-0x%04x "
"already in use!\n",acpi_base + SMB_STS, acpi_base + SMB_SAA);
acpi_base = 0; /* reset acpi_base */
return -ENODEV;
}
return 0;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = sis630_access,
.functionality = sis630_func,
};
static struct i2c_adapter sis630_adapter = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS630,
.algo = &smbus_algorithm,
.inc_use = sis630_inc,
.dec_use = sis630_dec,
};
static struct pci_device_id sis630_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_SI,
.device = PCI_DEVICE_ID_SI_503,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
if (sis630_setup(dev)) {
printk(KERN_ERR "i2c-sis630.o: SIS630 comp. bus not detected, module not inserted.\n");
return -ENODEV;
}
sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x",
acpi_base + SMB_STS);
return i2c_add_adapter(&sis630_adapter);
}
static void __devexit sis630_remove(struct pci_dev *dev)
{
if (acpi_base) {
i2c_del_adapter(&sis630_adapter);
release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION);
acpi_base = 0;
}
}
static struct pci_driver sis630_driver = {
.name = "sis630 smbus",
.id_table = sis630_ids,
.probe = sis630_probe,
.remove = __devexit_p(sis630_remove),
};
static int __init i2c_sis630_init(void)
{
printk("i2c-sis630.o version %s (%s)\n", LM_VERSION, LM_DATE);
return pci_module_init(&sis630_driver);
}
static void __exit i2c_sis630_exit(void)
{
pci_unregister_driver(&sis630_driver);
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>");
MODULE_DESCRIPTION("SIS630 SMBus driver");
module_init(i2c_sis630_init);
module_exit(i2c_sis630_exit);
-605
View File
@@ -1,605 +0,0 @@
/*
sis645.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2003 Mark M. Hoffman <mhoffman@lightlink.com>
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.
*/
/*
This module must be considered BETA unless and until
the chipset manufacturer releases a datasheet.
The register definitions are based on the SiS630.
The method for *finding* the registers is based on trial and error.
A history of changes to this file is available by SVN:
http://www.lm-sensors.org/wiki/Download
*/
/* 25th March 2004
Support for Sis655 chipsets added by Ken Healy
*/
/*
Note: we assume there can only be one SiS645 with one SMBus interface
*/
/* #define DEBUG 1 */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
#define DRV_NAME "i2c-sis645"
/* SiS645DX north bridge (defined in 2.4.21) */
#ifndef PCI_DEVICE_ID_SI_646
#define PCI_DEVICE_ID_SI_646 0x0646
#endif
/* SiS648 north bridge (defined in 2.4.21) */
#ifndef PCI_DEVICE_ID_SI_648
#define PCI_DEVICE_ID_SI_648 0x0648
#endif
/* SiS650 north bridge (defined in 2.4.19) */
#ifndef PCI_DEVICE_ID_SI_650
#define PCI_DEVICE_ID_SI_650 0x0650
#endif
/* SiS651 north bridge (defined in 2.4.21)*/
#ifndef PCI_DEVICE_ID_SI_651
#define PCI_DEVICE_ID_SI_651 0x0651
#endif
/* SiS655 north bridge (defined in 2.4.22)*/
#ifndef PCI_DEVICE_ID_SI_655
#define PCI_DEVICE_ID_SI_655 0x0655
#endif
/* SiS746 north bridge (defined in 2.4.21) */
#ifndef PCI_DEVICE_ID_SI_746
#define PCI_DEVICE_ID_SI_746 0x0746
#endif
/* SiS85C503/5513 (LPC Bridge) */
#ifndef PCI_DEVICE_ID_SI_LPC
#define PCI_DEVICE_ID_SI_LPC 0x0018
#endif
/* SiS961 south bridge */
#ifndef PCI_DEVICE_ID_SI_961
#define PCI_DEVICE_ID_SI_961 0x0961
#endif
/* SiS962 south bridge */
#ifndef PCI_DEVICE_ID_SI_962
#define PCI_DEVICE_ID_SI_962 0x0962
#endif
/* SiS963 south bridge */
#ifndef PCI_DEVICE_ID_SI_963
#define PCI_DEVICE_ID_SI_963 0x0963
#endif
/* SMBus ID */
#ifndef PCI_DEVICE_ID_SI_SMBUS
#define PCI_DEVICE_ID_SI_SMBUS 0x16
#endif
/* base address register in PCI config space */
#define SIS645_BAR 0x04
/* SiS645 SMBus registers */
#define SMB_STS 0x00
#define SMB_EN 0x01
#define SMB_CNT 0x02
#define SMB_HOST_CNT 0x03
#define SMB_ADDR 0x04
#define SMB_CMD 0x05
#define SMB_PCOUNT 0x06
#define SMB_COUNT 0x07
#define SMB_BYTE 0x08
#define SMB_DEV_ADDR 0x10
#define SMB_DB0 0x11
#define SMB_DB1 0x12
#define SMB_SAA 0x13
/* register count for request_region */
#define SMB_IOSIZE 0x20
/* Other settings */
#define MAX_TIMEOUT 500
/* SiS645 SMBus constants */
#define SIS645_QUICK 0x00
#define SIS645_BYTE 0x01
#define SIS645_BYTE_DATA 0x02
#define SIS645_WORD_DATA 0x03
#define SIS645_PROC_CALL 0x04
#define SIS645_BLOCK_DATA 0x05
static struct pci_driver sis645_driver;
static struct i2c_adapter sis645_adapter;
static u16 sis645_smbus_base = 0;
static inline u8 sis645_read(u8 reg)
{
return inb(sis645_smbus_base + reg) ;
}
static inline void sis645_write(u8 reg, u8 data)
{
outb(data, sis645_smbus_base + reg) ;
}
#ifdef CONFIG_HOTPLUG
/* Turns on SMBus device if it is not; return 0 iff successful
*/
static int __devinit sis645_enable_smbus(struct pci_dev *dev)
{
u8 val = 0;
pci_read_config_byte(dev, 0x77, &val);
#ifdef DEBUG
printk(KERN_DEBUG DRV_NAME ": Config byte was 0x%02x.\n", val);
#endif
pci_write_config_byte(dev, 0x77, val & ~0x10);
pci_read_config_byte(dev, 0x77, &val);
if (val & 0x10) {
#ifdef DEBUG
printk(KERN_DEBUG DRV_NAME ": Config byte stuck!\n");
#endif
return -1;
}
return 0;
}
/* Builds the basic pci_dev for SiS645 SMBus
*/
static int __devinit sis645_build_dev(struct pci_dev **smbus_dev,
struct pci_dev *bridge_dev)
{
struct pci_dev temp_dev;
u16 vid = 0, did = 0;
int ret;
/* fill in the device structure for search */
memset(&temp_dev, 0, sizeof(temp_dev));
temp_dev.bus = bridge_dev->bus;
temp_dev.sysdata = bridge_dev->bus->sysdata;
temp_dev.hdr_type = PCI_HEADER_TYPE_NORMAL;
/* the SMBus device is function 1 on the same unit as the ISA bridge */
temp_dev.devfn = bridge_dev->devfn + 1;
/* query to make sure */
ret = pci_read_config_word(&temp_dev, PCI_VENDOR_ID, &vid);
if (ret || PCI_VENDOR_ID_SI != vid) {
printk(KERN_ERR DRV_NAME ": Couldn't find SMBus device!\n");
return ret;
}
temp_dev.vendor = vid;
ret = pci_read_config_word(&temp_dev, PCI_DEVICE_ID, &did);
if (ret || PCI_DEVICE_ID_SI_SMBUS != did) {
printk(KERN_ERR DRV_NAME ": Couldn't find SMBus device!\n");
return ret;
}
temp_dev.device = did;
/* ok, we've got it... request some memory and finish it off */
*smbus_dev = kmalloc(sizeof(**smbus_dev), GFP_ATOMIC);
if (NULL == *smbus_dev) {
printk(KERN_ERR DRV_NAME ": Out of memory!\n");
return -ENOMEM;
}
**smbus_dev = temp_dev;
ret = pci_setup_device(*smbus_dev);
if (ret) {
printk(KERN_ERR DRV_NAME ": pci_setup_device failed (0x%08x)\n",ret);
}
return ret;
}
/* See if a SMBus can be found, and enable it if possible.
*/
static int __devinit sis645_hotplug_smbus(void)
{
int ret;
struct pci_dev *smbus_dev, *bridge_dev ;
if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_961, NULL)))
printk(KERN_INFO DRV_NAME ": Found SiS961 south bridge.\n");
else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_962, NULL)))
printk(KERN_INFO DRV_NAME ": Found SiS962 [MuTIOL Media IO].\n");
else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_963, NULL)))
printk(KERN_INFO DRV_NAME ": Found SiS963 [MuTIOL Media IO].\n");
else if ((bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_503, NULL)) ||
(bridge_dev = pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_LPC, NULL))) {
printk(KERN_INFO DRV_NAME ": Found SiS south bridge in compatability mode(?)\n");
/* look for known compatible north bridges */
if ((NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_645, NULL))
&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_646, NULL))
&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_648, NULL))
&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_650, NULL))
&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_651, NULL))
&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_655, NULL))
&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_735, NULL))
&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_745, NULL))
&& (NULL == pci_find_device(PCI_VENDOR_ID_SI,
PCI_DEVICE_ID_SI_746, NULL))) {
printk(KERN_ERR DRV_NAME ": Can't find suitable host bridge!\n");
return -ENODEV;
}
} else {
printk(KERN_ERR DRV_NAME ": Can't find suitable south bridge!\n");
return -ENODEV;
}
/* if we get this far, we think the smbus device is present */
if ((ret = sis645_enable_smbus(bridge_dev)))
return ret;
if ((ret = sis645_build_dev(&smbus_dev, bridge_dev)))
return ret;
if ((ret = pci_enable_device(smbus_dev))) {
printk(KERN_ERR DRV_NAME ": Can't pci_enable SMBus device!"
" (0x%08x)\n", ret);
return ret;
}
pci_insert_device(smbus_dev, smbus_dev->bus);
return 0;
}
#endif /* CONFIG_HOTPLUG */
/* Execute a SMBus transaction.
int size is from SIS645_QUICK to SIS645_BLOCK_DATA
*/
static int sis645_transaction(int size)
{
int temp;
int result = 0;
int timeout = 0;
/* Make sure the SMBus host is ready to start transmitting */
if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) {
#ifdef DEBUG
printk(KERN_DEBUG DRV_NAME ": SMBus busy (0x%02x). Resetting...\n",
temp);
#endif
/* kill the transaction */
sis645_write(SMB_HOST_CNT, 0x20);
/* check it again */
if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) {
#ifdef DEBUG
printk(KERN_DEBUG DRV_NAME ": Failed! (0x%02x)\n", temp);
#endif
return -1;
} else {
#ifdef DEBUG
printk(KERN_DEBUG DRV_NAME ": Successful!\n");
#endif
}
}
/* Turn off timeout interrupts, set fast host clock */
sis645_write(SMB_CNT, 0x20);
/* clear all (sticky) status flags */
temp = sis645_read(SMB_STS);
sis645_write(SMB_STS, temp & 0x1e);
/* start the transaction by setting bit 4 and size bits */
sis645_write(SMB_HOST_CNT, 0x10 | (size & 0x07));
/* We will always wait for a fraction of a second! */
do {
i2c_delay(1);
temp = sis645_read(SMB_STS);
} while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
printk(KERN_DEBUG DRV_NAME ": SMBus Timeout! (0x%02x)\n",temp);
result = -1;
}
/* device error - probably missing ACK */
if (temp & 0x02) {
#ifdef DEBUG
printk(KERN_DEBUG DRV_NAME ": Failed bus transaction!\n");
#endif
result = -1;
}
/* bus collision */
if (temp & 0x04) {
#ifdef DEBUG
printk(KERN_DEBUG DRV_NAME ": Bus collision!\n");
#endif
result = -1;
}
/* Finish up by resetting the bus */
sis645_write(SMB_STS, temp);
if ((temp = sis645_read(SMB_STS))) {
#ifdef DEBUG
printk(KERN_DEBUG DRV_NAME ": Failed reset at end of transaction!"
" (0x%02x)\n", temp);
#endif
}
return result;
}
/* Return -1 on error. */
static s32 sis645_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
switch (size) {
case I2C_SMBUS_QUICK:
sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
size = SIS645_QUICK;
break;
case I2C_SMBUS_BYTE:
sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
if (read_write == I2C_SMBUS_WRITE)
sis645_write(SMB_CMD, command);
size = SIS645_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
sis645_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE)
sis645_write(SMB_BYTE, data->byte);
size = SIS645_BYTE_DATA;
break;
case I2C_SMBUS_PROC_CALL:
case I2C_SMBUS_WORD_DATA:
sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
sis645_write(SMB_CMD, command);
if (read_write == I2C_SMBUS_WRITE) {
sis645_write(SMB_BYTE, data->word & 0xff);
sis645_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8);
}
size = (size == I2C_SMBUS_PROC_CALL ? SIS645_PROC_CALL : SIS645_WORD_DATA);
break;
case I2C_SMBUS_BLOCK_DATA:
/* TO DO: */
printk(KERN_INFO DRV_NAME ": SMBus block not implemented!\n");
return -1;
break;
default:
printk(KERN_INFO DRV_NAME ": Unsupported I2C size\n");
return -1;
break;
}
if (sis645_transaction(size))
return -1;
if ((size != SIS645_PROC_CALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS645_QUICK)))
return 0;
switch (size) {
case SIS645_BYTE:
case SIS645_BYTE_DATA:
data->byte = sis645_read(SMB_BYTE);
break;
case SIS645_WORD_DATA:
case SIS645_PROC_CALL:
data->word = sis645_read(SMB_BYTE) +
(sis645_read(SMB_BYTE + 1) << 8);
break;
}
return 0;
}
static void sis645_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void sis645_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static u32 sis645_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_PROC_CALL;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = sis645_access,
.functionality = sis645_func,
};
static struct i2c_adapter sis645_adapter = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS645,
.algo = &smbus_algorithm,
.inc_use = sis645_inc,
.dec_use = sis645_dec,
};
static struct pci_device_id sis645_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_SI,
.device = PCI_DEVICE_ID_SI_SMBUS,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit sis645_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
u16 ww = 0;
int retval;
if (sis645_smbus_base) {
dev_err(dev, "Only one device supported.\n");
return -EBUSY;
}
pci_read_config_word(dev, PCI_CLASS_DEVICE, &ww);
if (PCI_CLASS_SERIAL_SMBUS != ww) {
dev_err(dev, "Unsupported device class 0x%04x!\n", ww);
return -ENODEV;
}
sis645_smbus_base = pci_resource_start(dev, SIS645_BAR);
if (!sis645_smbus_base) {
dev_err(dev, "SiS645 SMBus base address "
"not initialized!\n");
return -EINVAL;
}
dev_info(dev, "SiS645 SMBus base address: 0x%04x\n",
sis645_smbus_base);
/* Everything is happy, let's grab the memory and set things up. */
if (!request_region(sis645_smbus_base, SMB_IOSIZE,
sis645_driver.name)) {
dev_err(dev, "SMBus registers 0x%04x-0x%04x "
"already in use!\n", sis645_smbus_base,
sis645_smbus_base + SMB_IOSIZE - 1);
sis645_smbus_base = 0;
return -EINVAL;
}
sprintf(sis645_adapter.name, "SiS645 SMBus adapter at 0x%04x",
sis645_smbus_base);
if ((retval = i2c_add_adapter(&sis645_adapter))) {
dev_err(dev, "Couldn't register adapter!\n");
release_region(sis645_smbus_base, SMB_IOSIZE);
sis645_smbus_base = 0;
}
return retval;
}
static void __devexit sis645_remove(struct pci_dev *dev)
{
if (sis645_smbus_base) {
i2c_del_adapter(&sis645_adapter);
release_region(sis645_smbus_base, SMB_IOSIZE);
sis645_smbus_base = 0;
}
}
static struct pci_driver sis645_driver = {
.name = "sis645 smbus",
.id_table = sis645_ids,
.probe = sis645_probe,
.remove = __devexit_p(sis645_remove),
};
static int __init i2c_sis645_init(void)
{
printk(KERN_INFO DRV_NAME ".o version %s (%s)\n", LM_VERSION, LM_DATE);
/* if the required device id is not present, try to HOTPLUG it first */
if (!pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_SMBUS, NULL)) {
printk(KERN_INFO DRV_NAME ": "
"Attempting to enable SiS645 SMBus device\n");
#ifdef CONFIG_HOTPLUG
sis645_hotplug_smbus();
#else
printk(KERN_INFO DRV_NAME ": "
"Requires kernel with CONFIG_HOTPLUG, sorry!\n");
#endif
}
return pci_module_init(&sis645_driver);
}
static void __exit i2c_sis645_exit(void)
{
pci_unregister_driver(&sis645_driver);
}
MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
MODULE_DESCRIPTION("SiS645 SMBus driver");
MODULE_LICENSE("GPL");
/* Register initialization functions using helper macros */
module_init(i2c_sis645_init);
module_exit(i2c_sis645_exit);
-170
View File
@@ -1,170 +0,0 @@
/*
i2c-tsunami.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 2001 Oleg Vdovikin <vdovikin@jscc.ru>
Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
Simon Vogl
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.
*/
/* This interfaces to the I2C bus of the Tsunami/Typhoon 21272 chipsets
to gain access to the on-board I2C devices.
For more information refer to Compaq's
"Tsunami/Typhoon 21272 Chipset Hardware Reference Manual"
Order Number: DS-0025-TE
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
#include <asm/core_tsunami.h>
#include "version.h"
#include "sensors_compat.h"
MODULE_LICENSE("GPL");
/* Memory Presence Detect Register (MPD-RW) bits
with except of reserved RAZ bits */
#define MPD_DR 0x8 /* Data receive - RO */
#define MPD_CKR 0x4 /* Clock receive - RO */
#define MPD_DS 0x2 /* Data send - Must be a 1 to receive - WO */
#define MPD_CKS 0x1 /* Clock send - WO */
static inline void writempd(unsigned long value)
{
TSUNAMI_cchip->mpd.csr = value;
mb();
}
static inline unsigned long readmpd(void)
{
return TSUNAMI_cchip->mpd.csr;
}
static void bit_tsunami_setscl(void *data, int val)
{
/* read currently setted bits to modify them */
unsigned long bits = readmpd() >> 2; /* assume output == input */
if (val)
bits |= MPD_CKS;
else
bits &= ~MPD_CKS;
writempd(bits);
}
static void bit_tsunami_setsda(void *data, int val)
{
/* read currently setted bits to modify them */
unsigned long bits = readmpd() >> 2; /* assume output == input */
if (val)
bits |= MPD_DS;
else
bits &= ~MPD_DS;
writempd(bits);
}
/* The MPD pins are open drain, so the pins always remain outputs.
We rely on the i2c-algo-bit routines to set the pins high before
reading the input from other chips. */
static int bit_tsunami_getscl(void *data)
{
return (0 != (readmpd() & MPD_CKR));
}
static int bit_tsunami_getsda(void *data)
{
return (0 != (readmpd() & MPD_DR));
}
static void i2c_tsunami_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void i2c_tsunami_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static struct i2c_algo_bit_data tsunami_i2c_bit_data = {
.setsda = bit_tsunami_setsda,
.setscl = bit_tsunami_setscl,
.getsda = bit_tsunami_getsda,
.getscl = bit_tsunami_getscl,
.udelay = 10,
.mdelay = 10,
.timeout = HZ/2
};
static struct i2c_adapter tsunami_i2c_adapter = {
.name = "I2C Tsunami/Typhoon adapter",
.id = I2C_HW_B_TSUNA,
.algo_data = &tsunami_i2c_bit_data,
.inc_use = i2c_tsunami_inc,
.dec_use = i2c_tsunami_dec,
};
#if 0
static struct pci_driver tsunami_driver = {
.name = "tsunami smbus",
.id_table = tsunami_ids,
.probe = tsunami_probe,
.remove = __devexit_p(tsunami_remove),
};
#endif
static int __init i2c_tsunami_init(void)
{
printk("i2c-tsunami.o version %s (%s)\n", LM_VERSION, LM_DATE);
if (hwrpb->sys_type != ST_DEC_TSUNAMI) {
printk("i2c-tsunami.o: not Tsunami based system (%ld), module not inserted.\n", hwrpb->sys_type);
return -ENXIO;
} else {
printk("i2c-tsunami.o: using Cchip MPD at 0x%lx.\n", (long) &TSUNAMI_cchip->mpd);
}
return i2c_bit_add_bus(&tsunami_i2c_adapter);
}
static void __exit i2c_tsunami_exit(void)
{
i2c_bit_del_bus(&tsunami_i2c_adapter);
}
MODULE_AUTHOR("Oleg I. Vdovikin <vdovikin@jscc.ru>");
MODULE_DESCRIPTION("Tsunami I2C/SMBus driver");
module_init(i2c_tsunami_init);
module_exit(i2c_tsunami_exit);
-199
View File
@@ -1,199 +0,0 @@
/*
i2c-via.c - Part of lm_sensors, Linux kernel modules
for hardware monitoring
i2c Support for Via Technologies 82C586B South Bridge
Copyright (c) 1998, 1999 Kyösti Mälkki <kmalkki@cc.hut.fi>
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 <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/param.h> /* for HZ */
#include "version.h"
#include "sensors_compat.h"
/* Power management registers */
#define PM_CFG_REVID 0x08 /* silicon revision code */
#define PM_CFG_IOBASE0 0x20
#define PM_CFG_IOBASE1 0x48
#define I2C_DIR (pm_io_base+0x40)
#define I2C_OUT (pm_io_base+0x42)
#define I2C_IN (pm_io_base+0x44)
#define I2C_SCL 0x02 /* clock bit in DIR/OUT/IN register */
#define I2C_SDA 0x04
/* io-region reservation */
#define IOSPACE 0x06
#define IOTEXT "via-i2c"
static u16 pm_io_base = 0;
/*
It does not appear from the datasheet that the GPIO pins are
open drain. So a we set a low value by setting the direction to
output and a high value by setting the direction to input and
relying on the required I2C pullup. The data value is initialized
to 0 in via_init() and never changed.
*/
static void bit_via_setscl(void *data, int state)
{
outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL,
I2C_DIR);
}
static void bit_via_setsda(void *data, int state)
{
outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA,
I2C_DIR);
}
static int bit_via_getscl(void *data)
{
return (0 != (inb(I2C_IN) & I2C_SCL));
}
static int bit_via_getsda(void *data)
{
return (0 != (inb(I2C_IN) & I2C_SDA));
}
static void bit_via_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void bit_via_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static struct i2c_algo_bit_data bit_data = {
.setsda = bit_via_setsda,
.setscl = bit_via_setscl,
.getsda = bit_via_getsda,
.getscl = bit_via_getscl,
.udelay = 5,
.mdelay = 5,
.timeout = HZ
};
static struct i2c_adapter vt586b_adapter = {
.name = "VIA i2c",
.id = I2C_HW_B_VIA,
.algo_data = &bit_data,
.inc_use = bit_via_inc,
.dec_use = bit_via_dec,
};
static struct pci_device_id vt586b_ids[] __initdata = {
{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0, }
};
static int __init vt586b_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
u16 base;
u8 rev;
int res;
if (pm_io_base) {
printk(KERN_ERR "i2c-via.o: Will only support one host\n");
return -EBUSY;
}
pci_read_config_byte(dev, PM_CFG_REVID, &rev);
switch (rev) {
case 0x00:
base = PM_CFG_IOBASE0;
break;
case 0x01:
case 0x10:
base = PM_CFG_IOBASE1;
break;
default:
base = PM_CFG_IOBASE1;
/* later revision */
}
pci_read_config_word(dev, base, &pm_io_base);
pm_io_base &= (0xff << 8);
if (! request_region(I2C_DIR, IOSPACE, IOTEXT)) {
printk("i2c-via.o: IO 0x%x-0x%x already in use\n",
I2C_DIR, I2C_DIR + IOSPACE);
return -EBUSY;
}
outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR);
outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT);
res = i2c_bit_add_bus(&vt586b_adapter);
if ( res < 0 ) {
release_region(I2C_DIR, IOSPACE);
pm_io_base = 0;
return res;
}
return 0;
}
static int __init i2c_vt586b_init(void)
{
struct pci_dev *dev;
const struct pci_device_id *id;
printk("i2c-via.o version %s (%s)\n", LM_VERSION, LM_DATE);
pci_for_each_dev(dev) {
id = pci_match_device(vt586b_ids, dev);
if(id)
if(vt586b_probe(dev, id) >= 0)
return 0;
}
return -ENODEV;
}
static void __exit i2c_vt586b_exit(void)
{
i2c_bit_del_bus(&vt586b_adapter);
release_region(I2C_DIR, IOSPACE);
}
MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>");
MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge");
MODULE_LICENSE("GPL");
module_init(i2c_vt586b_init);
module_exit(i2c_vt586b_exit);
-564
View File
@@ -1,564 +0,0 @@
/*
i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>, Kyösti Mälkki <kmalkki@cc.hut.fi>,
Mark D. Studebaker <mdsxyz123@yahoo.com>
Copyright (C) 2005 - 2007 Jean Delvare <khali@linux-fr.org>
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.
*/
/*
Supports the following VIA south bridges:
Chip name PCI ID REV I2C block
VT82C596A 0x3050 no
VT82C596B 0x3051 no
VT82C686A 0x3057 0x30 no
VT82C686B 0x3057 0x40 yes
VT8231 0x8235 no?
VT8233 0x3074 yes
VT8233A 0x3147 yes?
VT8235 0x3177 yes
VT8237R 0x3227 yes
VT8237A 0x3337 yes
VT8251 0x3287 yes
CX700 0x8324 yes
Note: we assume there can only be one device, with one SMBus interface.
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <asm/io.h>
#include "version.h"
#include "sensors_compat.h"
/* 8233A is undefined before kernel 2.4.19 */
#ifndef PCI_DEVICE_ID_VIA_8233A
#define PCI_DEVICE_ID_VIA_8233A 0x3147
#endif
/* 8235 is undefined before kernel 2.4.21 */
#ifndef PCI_DEVICE_ID_VIA_8235
#define PCI_DEVICE_ID_VIA_8235 0x3177
#endif
/* 8237 is undefined before kernel 2.4.21 */
#ifndef PCI_DEVICE_ID_VIA_8237
#define PCI_DEVICE_ID_VIA_8237 0x3227
#endif
/* 8237A is undefined */
#ifndef PCI_DEVICE_ID_VIA_8237A
#define PCI_DEVICE_ID_VIA_8237A 0x3337
#endif
/* 8251 is undefined */
#ifndef PCI_DEVICE_ID_VIA_8251
#define PCI_DEVICE_ID_VIA_8251 0x3287
#endif
/* CX700 is undefined */
#ifndef PCI_DEVICE_ID_VIA_CX700
#define PCI_DEVICE_ID_VIA_CX700 0x8324
#endif
#define SMBBA1 0x90
#define SMBBA2 0x80
#define SMBBA3 0xD0
/* SMBus address offsets */
static unsigned short vt596_smba;
#define SMBHSTSTS (vt596_smba + 0)
#define SMBHSTCNT (vt596_smba + 2)
#define SMBHSTCMD (vt596_smba + 3)
#define SMBHSTADD (vt596_smba + 4)
#define SMBHSTDAT0 (vt596_smba + 5)
#define SMBHSTDAT1 (vt596_smba + 6)
#define SMBBLKDAT (vt596_smba + 7)
/* PCI Address Constants */
/* SMBus data in configuration space can be found in two places,
We try to select the better one */
static unsigned short SMBHSTCFG = 0xD2;
/* Other settings */
#define MAX_TIMEOUT 500
/* VT82C596 constants */
#define VT596_QUICK 0x00
#define VT596_BYTE 0x04
#define VT596_BYTE_DATA 0x08
#define VT596_WORD_DATA 0x0C
#define VT596_BLOCK_DATA 0x14
#define VT596_I2C_BLOCK_DATA 0x34
/* If force is set to anything different from 0, we forcibly enable the
VT596. DANGEROUS! */
static int force;
MODULE_PARM(force, "i");
MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!");
/* If force_addr is set to anything different from 0, we forcibly enable
the VT596 at the given address. VERY DANGEROUS! */
static int force_addr;
MODULE_PARM(force_addr, "i");
MODULE_PARM_DESC(force_addr,
"Forcibly enable the SMBus at the given address. "
"EXTREMELY DANGEROUS!");
static struct i2c_adapter vt596_adapter;
#define FEATURE_I2CBLOCK (1<<0)
static unsigned int vt596_features;
#ifdef DEBUG
static void vt596_dump_regs(const char *msg, u8 size)
{
dev_dbg(&vt596_adapter, "%s: STS=%02x CNT=%02x CMD=%02x ADD=%02x "
"DAT=%02x,%02x\n", msg, inb_p(SMBHSTSTS), inb_p(SMBHSTCNT),
inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0),
inb_p(SMBHSTDAT1));
if (size == VT596_BLOCK_DATA
|| size == VT596_I2C_BLOCK_DATA) {
int i;
dev_dbg(&vt596_adapter, "BLK=");
for (i = 0; i < I2C_SMBUS_BLOCK_MAX / 2; i++)
printk("%02x,", inb_p(SMBBLKDAT));
printk("\n");
dev_dbg(&vt596_adapter, " ");
for (; i < I2C_SMBUS_BLOCK_MAX - 1; i++)
printk("%02x,", inb_p(SMBBLKDAT));
printk("%02x\n", inb_p(SMBBLKDAT));
}
}
#else
static inline void vt596_dump_regs(const char *msg, u8 size) { }
#endif
/* Return -1 on error, 0 on success */
static int vt596_transaction(u8 size)
{
int temp;
int result = 0;
int timeout = 0;
vt596_dump_regs("Transaction (pre)", size);
/* Make sure the SMBus host is ready to start transmitting */
if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
dev_dbg(&vt596_adapter, "SMBus busy (0x%02x). "
"Resetting...\n", temp);
outb_p(temp, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
dev_err(&vt596_adapter, "SMBus reset failed! "
"(0x%02x)\n", temp);
return -1;
}
}
/* Start the transaction by setting bit 6 */
outb_p(0x40 | size, SMBHSTCNT);
/* We will always wait for a fraction of a second */
do {
i2c_delay(1);
temp = inb_p(SMBHSTSTS);
} while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT));
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
result = -1;
dev_err(&vt596_adapter, "SMBus Timeout!\n");
}
if (temp & 0x10) {
result = -1;
dev_err(&vt596_adapter, "Transaction failed (0x%02x)\n", size);
}
if (temp & 0x08) {
result = -1;
dev_err(&vt596_adapter, "SMBus collision!\n");
}
if (temp & 0x04) {
int read = inb_p(SMBHSTADD) & 0x01;
result = -1;
/* The quick and receive byte commands are used to probe
for chips, so errors are expected, and we don't want
to frighten the user. */
if (!((size == VT596_QUICK && !read) ||
(size == VT596_BYTE && read)))
dev_err(&vt596_adapter, "Transaction error!\n");
}
/* Resetting status register */
if (temp & 0x1F)
outb_p(temp, SMBHSTSTS);
vt596_dump_regs("Transaction (post)", size);
return result;
}
/* Return -1 on error, 0 on success */
static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
int i;
switch (size) {
case I2C_SMBUS_QUICK:
size = VT596_QUICK;
break;
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
size = VT596_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0);
size = VT596_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0);
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1);
}
size = VT596_WORD_DATA;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (!(vt596_features & FEATURE_I2CBLOCK))
goto exit_unsupported;
if (read_write == I2C_SMBUS_READ)
outb_p(I2C_SMBUS_BLOCK_MAX, SMBHSTDAT0);
/* Fall through */
case I2C_SMBUS_BLOCK_DATA:
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
u8 len = data->block[0];
if (len > I2C_SMBUS_BLOCK_MAX)
len = I2C_SMBUS_BLOCK_MAX;
outb_p(len, SMBHSTDAT0);
inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= len; i++)
outb_p(data->block[i], SMBBLKDAT);
}
size = (size == I2C_SMBUS_I2C_BLOCK_DATA) ?
VT596_I2C_BLOCK_DATA : VT596_BLOCK_DATA;
break;
default:
goto exit_unsupported;
}
outb_p(((addr & 0x7f) << 1) | read_write, SMBHSTADD);
if (vt596_transaction(size)) /* Error in transaction */
return -1;
if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK))
return 0;
switch (size) {
case VT596_BYTE:
case VT596_BYTE_DATA:
data->byte = inb_p(SMBHSTDAT0);
break;
case VT596_WORD_DATA:
data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8);
break;
case VT596_I2C_BLOCK_DATA:
case VT596_BLOCK_DATA:
data->block[0] = inb_p(SMBHSTDAT0);
if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
data->block[0] = I2C_SMBUS_BLOCK_MAX;
inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= data->block[0]; i++)
data->block[i] = inb_p(SMBBLKDAT);
break;
}
return 0;
exit_unsupported:
dev_warn(&vt596_adapter, "Unsupported command invoked! (0x%02x)\n",
size);
return -1;
}
static void vt596_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
static void vt596_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static u32 vt596_func(struct i2c_adapter *adapter)
{
u32 func = I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
if (vt596_features & FEATURE_I2CBLOCK)
func |= I2C_FUNC_SMBUS_I2C_BLOCK;
return func;
}
static struct i2c_algorithm smbus_algorithm = {
.name = "Non-I2C SMBus adapter",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = vt596_access,
.functionality = vt596_func,
};
static struct i2c_adapter vt596_adapter = {
.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2,
.algo = &smbus_algorithm,
.inc_use = vt596_inc,
.dec_use = vt596_dec,
};
static int __init vt596_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
unsigned char temp;
int error = -ENODEV;
/* Determine the address of the SMBus areas */
if (force_addr) {
vt596_smba = force_addr & 0xfff0;
force = 0;
goto found;
}
if ((pci_read_config_word(pdev, id->driver_data, &vt596_smba)) ||
!(vt596_smba & 0x0001)) {
/* try 2nd address and config reg. for 596 */
if (id->device == PCI_DEVICE_ID_VIA_82C596_3 &&
!pci_read_config_word(pdev, SMBBA2, &vt596_smba) &&
(vt596_smba & 0x0001)) {
SMBHSTCFG = 0x84;
} else {
/* no matches at all */
dev_err(pdev, "Cannot configure "
"SMBus I/O Base address\n");
return -ENODEV;
}
}
vt596_smba &= 0xfff0;
if (vt596_smba == 0) {
dev_err(pdev, "SMBus base address "
"uninitialized - upgrade BIOS or use "
"force_addr=0xaddr\n");
return -ENODEV;
}
found:
if (!request_region(vt596_smba, 8, "viapro-smbus")) {
dev_err(pdev, "SMBus region 0x%x already in use!\n",
vt596_smba);
return -ENODEV;
}
pci_read_config_byte(pdev, SMBHSTCFG, &temp);
/* If force_addr is set, we program the new address here. Just to make
sure, we disable the VT596 first. */
if (force_addr) {
pci_write_config_byte(pdev, SMBHSTCFG, temp & 0xfe);
pci_write_config_word(pdev, id->driver_data, vt596_smba);
pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01);
dev_warn(pdev, "WARNING: SMBus interface set to new "
"address 0x%04x!\n", vt596_smba);
} else if (!(temp & 0x01)) {
if (force) {
/* NOTE: This assumes I/O space and other allocations
* WERE done by the Bios! Don't complain if your
* hardware does weird things after enabling this.
* :') Check for Bios updates before resorting to
* this.
*/
pci_write_config_byte(pdev, SMBHSTCFG, temp | 0x01);
dev_info(pdev, "Enabling SMBus device\n");
} else {
dev_err(pdev, "SMBUS: Error: Host SMBus "
"controller not enabled! - upgrade BIOS or "
"use force=1\n");
goto release_region;
}
}
dev_dbg(pdev, "VT596_smba = 0x%X\n", vt596_smba);
switch (id->device) {
case PCI_DEVICE_ID_VIA_CX700:
case PCI_DEVICE_ID_VIA_8251:
case PCI_DEVICE_ID_VIA_8237:
case PCI_DEVICE_ID_VIA_8237A:
case PCI_DEVICE_ID_VIA_8235:
case PCI_DEVICE_ID_VIA_8233A:
case PCI_DEVICE_ID_VIA_8233_0:
vt596_features |= FEATURE_I2CBLOCK;
break;
case PCI_DEVICE_ID_VIA_82C686_4:
/* The VT82C686B (rev 0x40) does support I2C block
transactions, but the VT82C686A (rev 0x30) doesn't */
if (!pci_read_config_byte(pdev, PCI_REVISION_ID, &temp)
&& temp >= 0x40)
vt596_features |= FEATURE_I2CBLOCK;
break;
}
snprintf(vt596_adapter.name, 32,
"SMBus Via Pro adapter at %04x", vt596_smba);
return i2c_add_adapter(&vt596_adapter);
release_region:
release_region(vt596_smba, 8);
return error;
}
static struct pci_device_id vt596_ids[] __initdata = {
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_82C596_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA1,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_82C596B_3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA1,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_82C686_4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA1,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8233_0,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8233A,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8235,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8237,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8237A,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8231_4,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA1,
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_8251,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3
},
{
.vendor = PCI_VENDOR_ID_VIA,
.device = PCI_DEVICE_ID_VIA_CX700,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = SMBBA3
},
{ 0, }
};
static int __init i2c_vt596_init(void)
{
struct pci_dev *dev;
const struct pci_device_id *id;
printk("i2c-viapro.o version %s (%s)\n", LM_VERSION, LM_DATE);
pci_for_each_dev(dev) {
id = pci_match_device(vt596_ids, dev);
if(id)
if(vt596_probe(dev, id) >= 0)
return 0;
}
return -ENODEV;
}
static void __exit i2c_vt596_exit(void)
{
i2c_del_adapter(&vt596_adapter);
release_region(vt596_smba, 8);
}
MODULE_AUTHOR("Kyosti Malkki <kmalkki@cc.hut.fi>, "
"Mark D. Studebaker <mdsxyz123@yahoo.com> and "
"Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("vt82c596 SMBus driver");
MODULE_LICENSE("GPL");
module_init(i2c_vt596_init);
module_exit(i2c_vt596_exit);
-430
View File
@@ -1,430 +0,0 @@
/*
* i2c-virtual.c - Virtual I2C bus driver.
*
* Simplifies access to complex multiplexed I2C bus topologies, by presenting
* each multiplexed bus segment as a virtual I2C adapter. Supports multi-level
* mux'ing (mux behind a mux), as well as arbitration for exclusive bus access
* for those systems which require it (some bladed chassis for example).
*
* Copyright (c) 2004 Google, Inc. (Ken Harrenstien)
*
* Based on:
* i2c-virtual.c from Brian Kuschak <bkuschak@yahoo.com>
* which was:
* Adapted from i2c-adap-ibm_ocp.c
* Original file Copyright 2000-2002 MontaVista Software Inc.
*
* 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 <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include "i2c-virtual.h"
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#ifdef DEBUG
# define DBG(x) x
#else
# define DBG(x) {}
#endif
#define I2C_VIRT_PROCMAP 0 /* 1 for /proc/driver/i2c/virtual_i2c_map */
#define VIRT_TIMEOUT (HZ/2) /* 500msec */
#define VIRT_RETRIES 3
/* exclusive access to the bus */
#define I2C_LOCK(adap) down(&adap->bus)
#define I2C_UNLOCK(adap) up(&adap->bus)
#if I2C_VIRT_PROCMAP
/* pointer to the list of i2c_bus_mappings */
static LIST_HEAD(i2c_map_list);
struct i2c_bus_mapping {
struct i2c_adapter *parent;
struct i2c_adapter *virt;
unsigned long mux_addr;
unsigned long mux_val;
struct list_head list;
};
static void *i2c_virt_add_map(struct i2c_adapter *parent,
struct i2c_adapter *virt,
unsigned long mux_addr,
unsigned long mux_val);
#endif
/* First the I2C 'algorithm' driver code: */
/*
* Description: acquire exclusive access (if needed), select the mux,
* perform the transfer on the parent i2c adapter, deselect mux and drop
* exclusive access.
*/
static int
i2c_virt_master_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct i2c_virt_priv *priv = (struct i2c_virt_priv *)adap->data;
struct i2c_adapter *parent = priv->parent_adap;
int ret;
#ifdef I2C_REQUIRE_ARBITRATION
/* Acquire exclusive access to a shared I2C bus _before_ taking the
local I2C lock to prevent stalling local I2C transactions while
waiting for exclusive access.
*/
if(parent->algo->acquire_exclusive)
if ((ret = parent->algo->acquire_exclusive(parent)) < 0)
return ret;
#endif
/* Grab the lock for the parent adapter. We already hold the lock for
the virtual adapter. Then select the right mux port and perform
the transfer.
*/
DBG(printk(KERN_DEBUG "%s: Locking parent bus %0lX\n",
__FUNCTION__, (unsigned long)parent));
I2C_LOCK(parent);
if ((ret = priv->mux.select(parent, &priv->mux)) == 0) {
ret = parent->algo->master_xfer(parent, msgs, num);
}
(void) priv->mux.deselect(parent, &priv->mux);
I2C_UNLOCK(parent);
DBG(printk(KERN_DEBUG "%s: Unlocked parent bus %0lX\n",
__FUNCTION__, (unsigned long)parent));
#ifdef I2C_REQUIRE_ARBITRATION
{
int ret2;
/* Release exclusive bus access */
if(parent->algo->release_exclusive)
if ((ret2 = parent->algo->release_exclusive(parent)) < 0)
return ret2;
}
#endif
return ret;
}
static int
i2c_virt_smbus_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
struct i2c_virt_priv *priv = (struct i2c_virt_priv *)adap->data;
struct i2c_adapter *parent = priv->parent_adap;
int ret;
#ifdef I2C_REQUIRE_ARBITRATION
/* Acquire exclusive access to a shared I2C bus _before_ taking the
local I2C lock to prevent stalling local I2C transactions while
waiting for exclusive access.
*/
if(parent->algo->acquire_exclusive)
if ((ret = parent->algo->acquire_exclusive(parent)) < 0)
return ret;
#endif
/* Grab the lock for the parent adapter. We already hold the lock for
the virtual adapter. Then select the right mux port and perform
the transfer.
*/
DBG(printk(KERN_DEBUG "%s: Locking parent bus %0lX\n",
__FUNCTION__, (unsigned long)parent));
I2C_LOCK(parent);
if ((ret = priv->mux.select(parent, &priv->mux)) == 0) {
ret = parent->algo->smbus_xfer(parent, addr, flags,
read_write, command, size, data);
}
(void) priv->mux.deselect(parent, &priv->mux);
I2C_UNLOCK(parent);
DBG(printk(KERN_DEBUG "%s: Unlocked parent bus %0lX\n",
__FUNCTION__, (unsigned long)parent));
#ifdef I2C_REQUIRE_ARBITRATION
{
int ret2;
/* Release exclusive bus access */
if(parent->algo->release_exclusive)
if ((ret2 = parent->algo->release_exclusive(parent)) < 0)
return ret2;
}
#endif
return ret;
}
/*
* Description: Implements device specific ioctls.
* We don't support any yet.
*/
static int
i2c_virt_algo_control(struct i2c_adapter *adap, unsigned int cmd,
unsigned long arg)
{
return 0;
}
/* Virtual adapter functionality; returns functionality of parent adapter.
* OK if parent itself is virtual, as this will go all the way up to the
* real adapter.
*/
static u32
i2c_virt_functionality(struct i2c_adapter *adap)
{
struct i2c_virt_priv *priv = (struct i2c_virt_priv *)adap->data;
struct i2c_adapter *parent = priv->parent_adap;
return parent->algo->functionality(parent);
}
/* ===================================================================== */
/* The 'adapter' driver code: */
static int
i2c_virt_reg(struct i2c_client *client)
{
return 0;
}
static int
i2c_virt_unreg(struct i2c_client *client)
{
return 0;
}
/*
* Called to create a 'virtual' i2c bus which represents a multiplexed bus
* segment. Client and mux_val are passed to the select and deselect
* callback functions to perform hardware-specific mux control.
*/
struct i2c_adapter *i2c_virt_create_adapter(struct i2c_adapter *parent_adap,
struct i2c_client *client,
unsigned long mux_val,
void *select_cb,
void *deselect_cb)
{
struct i2c_adapter *adap;
struct i2c_virt_priv *priv;
struct i2c_algorithm *algo;
if (!(adap = kmalloc(sizeof (struct i2c_adapter)
+ sizeof(struct i2c_virt_priv)
+ sizeof(struct i2c_algorithm),
GFP_KERNEL))) {
printk(KERN_ERR "i2c_virt_register_adap: Failed allocation\n");
return NULL;
}
memset(adap, 0, sizeof(struct i2c_adapter)
+ sizeof(struct i2c_virt_priv)
+ sizeof(struct i2c_algorithm));
priv = (struct i2c_virt_priv *) (adap+1);
algo = (struct i2c_algorithm *) (priv+1);
/* Set up private adapter data */
priv->parent_adap = parent_adap;
priv->mux.client = client;
priv->mux.addr = client->addr;
priv->mux.value = mux_val;
priv->mux.select = select_cb;
priv->mux.deselect = deselect_cb;
/* Need to do algo dynamically because we don't know ahead
of time what sort of physical adapter we'll be dealing with.
*/
algo->id = I2C_ALGO_VIRT;
strcpy(algo->name, "Virtual I2C algorithm driver");
algo->master_xfer = (parent_adap->algo->master_xfer
? i2c_virt_master_xfer : NULL);
algo->smbus_xfer = (parent_adap->algo->smbus_xfer
? i2c_virt_smbus_xfer : NULL);
algo->slave_send = NULL;
algo->slave_recv = NULL;
algo->algo_control = i2c_virt_algo_control;
algo->functionality = i2c_virt_functionality;
#ifdef I2C_REQUIRE_ARBITRATION
algo->acquire_exclusive = NULL;
algo->release_exclusive = NULL;
#endif
/* Now fill out new adapter structure */
adap->inc_use = NULL;
adap->dec_use = NULL;
snprintf(adap->name, sizeof(adap->name),
"Virtual I2C (i2c-%d, mux %02lx:%02lx)",
i2c_adapter_id(parent_adap),
(unsigned long)client->addr, mux_val);
adap->id = I2C_HW_VIRT | algo->id;
adap->algo = algo;
adap->algo_data = NULL; /* XXX: Use this?? */
adap->client_register = i2c_virt_reg;
adap->client_unregister = i2c_virt_unreg;
adap->data = priv;
adap->flags = 0;
adap->timeout = VIRT_TIMEOUT;
adap->retries = VIRT_RETRIES;
DBG(printk(KERN_DEBUG "%s: Adding virt bus %0lX\n",
__FUNCTION__, (unsigned long)adap));
if (i2c_add_adapter_nolock(adap) < 0) {
kfree(adap);
return NULL;
}
printk(KERN_NOTICE "i2c%d: Virtual I2C bus "
"(Physical bus i2c%d, multiplexer %02lx port %ld)\n",
i2c_adapter_id(adap), i2c_adapter_id(parent_adap),
priv->mux.addr, mux_val);
#if I2C_VIRT_PROCMAP
i2c_virt_add_map(priv->parent_adap, adap,
priv->mux.addr, priv->value);
#endif
MOD_INC_USE_COUNT;
DBG(printk(KERN_DEBUG "%s: Added virt bus %0lX\n",
__FUNCTION__, (unsigned long)adap));
return adap;
}
int i2c_virt_remove_adapter(struct i2c_adapter *adap)
{
int ret;
DBG(printk(KERN_DEBUG "%s: Removing virt bus %0lX\n",
__FUNCTION__, (unsigned long)adap));
if ((ret = i2c_del_adapter_nolock(adap)) < 0)
return ret;
kfree(adap);
MOD_DEC_USE_COUNT;
return 0;
}
/* ------------------------------------------------------------------ */
#if I2C_VIRT_PROCMAP
/* Dubious stuff to implement a /proc/driver/i2c/virtual_i2c_map
* listing of all virtual busses.
*
* XXX: LOCKING???
*/
static int i2c_virt_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
char *buf = page;
struct list_head *l;
struct i2c_bus_mapping *pmap;
/* XXX: BUFFER BOUNDS CHECKING? */
buf += sprintf(buf, "i2c bus_mapping\n");
list_for_each(l, &i2c_map_list) {
pmap = list_entry(l, struct i2c_bus_mapping, list);
if(pmap)
buf += sprintf(buf, "base=iic%d mux_addr=%02hx, mux_val=%02hx => iic%d\n",
i2c_adapter_id(pmap->parent),
pmap->mux_addr, pmap->mux_val,
i2c_adapter_id(pmap->virt));
}
len = buf - page;
if (len <= off+count) *eof = 1;
*start = page + off;
len -= off;
if (len>count) len = count;
if (len<0) len = 0;
return len;
}
static void *i2c_virt_add_map(struct i2c_adapter *parent,
struct i2c_adapter *virt,
unsigned long mux_addr,
unsigned long mux_val)
{
struct i2c_bus_mapping *pmap;
if((pmap = kmalloc(sizeof(struct i2c_bus_mapping), GFP_KERNEL))
== NULL) {
printk(KERN_ERR "%s: Failed to allocate bus mapping\n",
__FUNCTION__);
return;
}
pmap->virt = virt;
pmap->parent = parent;
pmap->mux_addr = mux_addr;
pmap->mux_val = mux_val;
if (list_empty(&i2c_map_list)) {
/* If first time, ensure proc entry exists */
/* XXX: Maybe do this with module_init() */
create_proc_read_entry("driver/i2c/virtual_i2c_map", 0, NULL,
&i2c_virt_read_proc, NULL);
}
list_add_tail(&pmap->list, &i2c_map_list);
}
#if 0 /* Not used by anything, but kept for a while in case */
/* Called to find out which i2c bus to use to get to a specific bus segment.
*/
struct i2c_adapter *i2c_lookup_adapter(struct i2c_adapter *base,
unsigned long mux_addr,
unsigned long mux_val)
{
struct list_head *l;
struct i2c_bus_mapping *pmap;
list_for_each(l, &i2c_map_list) {
pmap = list_entry(l, struct i2c_bus_mapping, list);
if(pmap->parent == base
&& pmap->mux_addr == mux_addr
&& pmap->mux_val == mux_val)
return pmap->virt;
}
return NULL; /* none found */
}
EXPORT_SYMBOL(i2c_lookup_adapter);
#endif /* 0 */
#endif /* I2C_VIRT_PROCMAP */
EXPORT_SYMBOL(i2c_virt_create_adapter);
EXPORT_SYMBOL(i2c_virt_remove_adapter);
MODULE_AUTHOR("Ken Harrenstien (from Brian Kuschak)");
MODULE_DESCRIPTION("Virtual I2C driver for multiplexed I2C busses");
MODULE_LICENSE("GPL");
-296
View File
@@ -1,296 +0,0 @@
/*
voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware
monitoring
Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl>,
Philip Edelbrock <phil@netroedge.com>,
Ralph Metzler <rjkm@thp.uni-koeln.de>, and
Mark D. Studebaker <mdsxyz123@yahoo.com>
Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
Simon Vogl
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.
*/
/* This interfaces to the I2C bus of the Voodoo3 to gain access to
the BT869 and possibly other I2C devices. */
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/param.h> /* for HZ */
#include "version.h"
#include "sensors_compat.h"
MODULE_LICENSE("GPL");
/* the only registers we use */
#define REG 0x78
#define REG2 0x70
/* bit locations in the register */
#define DDC_ENAB 0x00040000
#define DDC_SCL_OUT 0x00080000
#define DDC_SDA_OUT 0x00100000
#define DDC_SCL_IN 0x00200000
#define DDC_SDA_IN 0x00400000
#define I2C_ENAB 0x00800000
#define I2C_SCL_OUT 0x01000000
#define I2C_SDA_OUT 0x02000000
#define I2C_SCL_IN 0x04000000
#define I2C_SDA_IN 0x08000000
/* initialization states */
#define INIT2 0x2
#define INIT3 0x4
/* delays */
#define CYCLE_DELAY 10
#define TIMEOUT (HZ / 2)
static void config_v3(struct pci_dev *dev);
static unsigned long ioaddr;
/* The voo GPIO registers don't have individual masks for each bit
so we always have to read before writing. */
static void bit_vooi2c_setscl(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if(val)
r |= I2C_SCL_OUT;
else
r &= ~I2C_SCL_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
static void bit_vooi2c_setsda(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if(val)
r |= I2C_SDA_OUT;
else
r &= ~I2C_SDA_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
/* The GPIO pins are open drain, so the pins always remain outputs.
We rely on the i2c-algo-bit routines to set the pins high before
reading the input from other chips. */
static int bit_vooi2c_getscl(void *data)
{
return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
}
static int bit_vooi2c_getsda(void *data)
{
return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
}
static void bit_vooddc_setscl(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if(val)
r |= DDC_SCL_OUT;
else
r &= ~DDC_SCL_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
static void bit_vooddc_setsda(void *data, int val)
{
unsigned int r;
r = readl(ioaddr + REG);
if(val)
r |= DDC_SDA_OUT;
else
r &= ~DDC_SDA_OUT;
writel(r, ioaddr + REG);
readl(ioaddr + REG); /* flush posted write */
}
static int bit_vooddc_getscl(void *data)
{
return (0 != (readl(ioaddr + REG) & DDC_SCL_IN));
}
static int bit_vooddc_getsda(void *data)
{
return (0 != (readl(ioaddr + REG) & DDC_SDA_IN));
}
/* Configures the chip */
void config_v3(struct pci_dev *dev)
{
unsigned int cadr;
/* map Voodoo3 memory */
cadr = dev->resource[0].start;
cadr &= PCI_BASE_ADDRESS_MEM_MASK;
ioaddr = (unsigned long)ioremap_nocache(cadr, 0x1000);
if(ioaddr) {
writel(0x8160, ioaddr + REG2);
writel(0xcffc0020, ioaddr + REG);
printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%lx\n", ioaddr);
}
}
static void voodoo3_inc(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_INC_USE_COUNT;
#endif
}
void voodoo3_dec(struct i2c_adapter *adapter)
{
#ifdef MODULE
MOD_DEC_USE_COUNT;
#endif
}
static struct i2c_algo_bit_data voo_i2c_bit_data = {
.setsda = bit_vooi2c_setsda,
.setscl = bit_vooi2c_setscl,
.getsda = bit_vooi2c_getsda,
.getscl = bit_vooi2c_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
static struct i2c_adapter voodoo3_i2c_adapter = {
.name = "I2C Voodoo3/Banshee adapter",
.id = I2C_HW_B_VOO,
.algo_data = &voo_i2c_bit_data,
.inc_use = voodoo3_inc,
.dec_use = voodoo3_dec,
};
static struct i2c_algo_bit_data voo_ddc_bit_data = {
.setsda = bit_vooddc_setsda,
.setscl = bit_vooddc_setscl,
.getsda = bit_vooddc_getsda,
.getscl = bit_vooddc_getscl,
.udelay = CYCLE_DELAY,
.mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
static struct i2c_adapter voodoo3_ddc_adapter = {
.name = "DDC Voodoo3/Banshee adapter",
.id = I2C_HW_B_VOO,
.algo_data = &voo_ddc_bit_data,
.inc_use = voodoo3_inc,
.dec_use = voodoo3_dec,
};
static struct pci_device_id voodoo3_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_3DFX,
.device = PCI_DEVICE_ID_3DFX_VOODOO3,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{
.vendor = PCI_VENDOR_ID_3DFX,
.device = PCI_DEVICE_ID_3DFX_BANSHEE,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ 0, }
};
static int __devinit voodoo3_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int retval;
config_v3(dev);
retval = i2c_bit_add_bus(&voodoo3_i2c_adapter);
if(retval)
return retval;
retval = i2c_bit_add_bus(&voodoo3_ddc_adapter);
if(retval)
i2c_bit_del_bus(&voodoo3_i2c_adapter);
return retval;
}
static void __devexit voodoo3_remove(struct pci_dev *dev)
{
i2c_bit_del_bus(&voodoo3_i2c_adapter);
i2c_bit_del_bus(&voodoo3_ddc_adapter);
}
/* Don't register driver to avoid driver conflicts */
/*
static struct pci_driver voodoo3_driver = {
.name = "voodoo3 smbus",
.id_table = voodoo3_ids,
.probe = voodoo3_probe,
.remove = __devexit_p(voodoo3_remove),
};
*/
static int __init i2c_voodoo3_init(void)
{
struct pci_dev *dev;
const struct pci_device_id *id;
printk("i2c-voodoo3.o version %s (%s)\n", LM_VERSION, LM_DATE);
/*
return pci_module_init(&voodoo3_driver);
*/
pci_for_each_dev(dev) {
id = pci_match_device(voodoo3_ids, dev);
if(id)
if(voodoo3_probe(dev, id) >= 0)
return 0;
}
return -ENODEV;
}
static void __exit i2c_voodoo3_exit(void)
{
/*
pci_unregister_driver(&voodoo3_driver);
*/
voodoo3_remove(NULL);
iounmap((void *)ioaddr);
}
MODULE_AUTHOR
("Frodo Looijaard <frodol@dds.nl>, Philip Edelbrock <phil@netroedge.com>, Ralph Metzler <rjkm@thp.uni-koeln.de>, and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver");
module_init(i2c_voodoo3_init);
module_exit(i2c_voodoo3_exit);
-836
View File
@@ -1,836 +0,0 @@
/*
* pca954x.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* This module supports the PCA954x series of I2C multiplexer/switch chips
* made by Philips Semiconductors. This includes the
* PCA9540, PCA9542, PCA9543, PCA9544, PCA9545, PCA9546, and PCA9548.
*
* Copyright (c) 2004 Google, Inc. (Ken Harrenstien)
*
* Based on:
* i2c-virtual_cb.c from Brian Kuschak <bkuschak@yahoo.com>
* and
* pca9540.c from Jean Delvare <khali@linux-fr.org>, which was
* based on pcf8574.c from the same project by Frodo Looijaard,
* Philip Edelbrock, Dan Eaton and Aurelien Jarno.
*
* These chips are all controlled via the I2C bus itself, and all have a
* single 8-bit register (normally at 0x70). The upstream "parent" bus fans
* out to two, four, or eight downstream busses or channels; which of these
* are selected is determined by the chip type and register contents. A
* mux can select only one sub-bus at a time; a switch can select any
* combination simultaneously.
*
* See Documentation/i2c/virtual_i2c for details on the virtual bus/adapter
* mechanism.
*
*********************************************************************
*
* 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 <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-proc.h>
#include <linux/init.h>
#include "i2c-virtual.h"
#include "version.h"
MODULE_LICENSE("GPL");
#define DEBUG 1 /* XXX: Comment out before release? */
#ifdef DEBUG
# define DBG(x) if (pca954x_debug > 0) { x; } else
#else
# define DBG(x) {}
#endif
#define PCA954X_NCHANS 8 /* Max # of channels (PCA9548) */
/* Addresses to scan */
static unsigned short normal_i2c[] = { /*0x70,*/ SENSORS_I2C_END };
static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
static unsigned int normal_isa[] = { SENSORS_ISA_END };
static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
/* Insmod/modprobe parameters */
/* Chip type must normally be specified using a parameter of the form
"force_pca9544=0,0x70"
The following declares the possible types.
*/
SENSORS_INSMOD_7(pca9540,pca9542,pca9543,pca9544,pca9545,pca9546,pca9548);
static int pca954x_debug;
MODULE_PARM(pca954x_debug, "i");
MODULE_PARM_DESC(pca954x_debug, "Debug output level (0 = none)");
#if 0 /* Considered adding this, but too many issues. See doc. */
static int pca954x_selmask[32]; /* Max # chips supported, sigh */
MODULE_PARM(pca954x_selmask, "1-32i");
MODULE_PARM_DESC(pca954x_selmask, "List of selmask settings for each mux/swi");
#endif
/* Provide specs for the PCA954x types we know about
*/
static struct pca954x_chipdef {
enum chips type;
const char *type_name;
int pcanum;
int nchans;
enum muxtype { pca954x_ismux=0, pca954x_isswi } muxtype;
} pca954x_chipdefs[] = {
{ pca9540, "pca9540", 9540, 2, pca954x_ismux },
{ pca9542, "pca9542", 9542, 2, pca954x_ismux },
{ pca9543, "pca9543", 9543, 2, pca954x_isswi },
{ pca9544, "pca9544", 9544, 4, pca954x_ismux },
{ pca9545, "pca9545", 9545, 4, pca954x_isswi },
{ pca9546, "pca9546", 9546, 4, pca954x_isswi },
{ pca9548, "pca9548", 9548, 8, pca954x_isswi },
};
#define PCA954X_MUX_ENA 0x04 /* Mux enable bit */
/* Each client has this additional data.
Note the two conventions for identifying a chip's channel (sub-bus):
"channel" 0 to N-1, where N is the number of channels supported
"bitmask" 1 to 1 << N-1, so bit 0 is channel 0.
This use of "channel" is consistent with that in the Philips PCA954X doc.
*/
struct pca954x_data {
struct i2c_client client;
int sysctl_id;
enum chips type;
enum muxtype muxtype;
int pcanum; /* PCA chip number */
int nchans; /* # of channels (busses) this chip controls */
int nchmask; /* Mask of all channels */
struct semaphore update_lock;
int selmask; /* Bitmask of chans OK to leave on (chn1 == bit0) */
int curmask; /* Bitmask of chans currently selected */
u8 regval; /* Current chip register value */
u8 origval; /* Original value at initialization */
struct i2c_adapter *virt_adapters[PCA954X_NCHANS];
};
static int pca954x_attach_adapter(struct i2c_adapter *adapter);
static int pca954x_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind);
static int pca954x_detach_client(struct i2c_client *client);
static void pca954x_pe_busses(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_debug(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_regval(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_selbus(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_selmask(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_pe_type(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results);
static void pca954x_set_selmask(struct i2c_client *client, int mask);
static u8 pca954x_mask2val(struct i2c_client *client, int mask);
static int pca954x_val2mask(struct i2c_client *client, u8 val);
static int pca954x_select_mux(struct i2c_adapter *adap,
struct i2c_mux_ctrl *mux);
static int pca954x_deselect_mux(struct i2c_adapter *adap,
struct i2c_mux_ctrl *mux);
static int pca954x_select_chan(struct i2c_adapter *adap,
struct i2c_client *client, unsigned long chan);
static int pca954x_xfer(struct i2c_adapter *adap,
struct i2c_client *client,
int read_write, u8 *val);
/* This is the driver that will be inserted */
static struct i2c_driver pca954x_driver = {
.name = "PCA954X I2C mux/switch driver",
.flags = I2C_DF_NOTIFY,
.attach_adapter = pca954x_attach_adapter,
.detach_client = pca954x_detach_client,
};
/* -- SENSORS SYSCTL START -- */
#define PCA954X_SYSCTL_BUSSES 1000
#define PCA954X_SYSCTL_DEBUG 1001
#define PCA954X_SYSCTL_REGVAL 1002
#define PCA954X_SYSCTL_SELBUS 1003
#define PCA954X_SYSCTL_SELMASK 1004
#define PCA954X_SYSCTL_TYPE 1005
/* -- SENSORS SYSCTL END -- */
static ctl_table pca954x_dir_table_template[] = {
{PCA954X_SYSCTL_BUSSES, "busses", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_busses},
{PCA954X_SYSCTL_DEBUG, "debug", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_debug},
{PCA954X_SYSCTL_REGVAL, "regval", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_regval},
{PCA954X_SYSCTL_SELBUS, "selbus", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_selbus},
{PCA954X_SYSCTL_SELMASK, "selmask", NULL, 0, 0644, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_selmask},
{PCA954X_SYSCTL_TYPE, "type", NULL, 0, 0444, NULL,
&i2c_proc_real, &i2c_sysctl_real, NULL, &pca954x_pe_type},
{0}
};
static int pca954x_id = 0;
static int __init pca954x_init(void)
{
printk("pca954x.o version %s (%s)\n", LM_VERSION, LM_DATE);
/* Because we've set I2C_DF_NOTIFY in our driver struct,
* this call results in calling pca954x_attach_adapter() for
* every adapter currently known, both real and virtual.
*/
return i2c_add_driver(&pca954x_driver);
}
static void __exit pca954x_exit(void)
{
i2c_del_driver(&pca954x_driver);
}
/* This function is called whenever a new bus/adapter is added, OR when
the driver itself is added (for all existing adapters), to see if
the driver can find a chip address it's responsible for.
*/
static int pca954x_attach_adapter(struct i2c_adapter *adapter)
{
DBG(printk(KERN_DEBUG "%s: %0lX\n",
__FUNCTION__, (unsigned long)adapter));
/* Apply standard lookup via parameters or probing.
"addr_data" is defined by the SENSORS_INSMOD_n macro.
*/
return i2c_detect(adapter, &addr_data, pca954x_detect);
}
/* This function is called by i2c_detect and must be of type
"i2c_found_addr_proc" as defined in i2c-proc.h.
For the PCA954x series this should only happen due to "force" parameters
given to insmod/modprobe, as there is no way to autodetect what flavor
of chip we have (and the differences matter!)
Thus, we should never get a "kind" of -1.
Note the unique potential for infinite recursion here because we are
creating new adapters (one for each muxed virtual bus). This will cause
our attach_adapter() function to be called twice for each new adapter,
because:
(1) The call to i2c_add_adapter results in calling the attach_adapter()
function of all known chip drivers, including this one.
(2) Because normally this is first called via i2c_add_driver calling
i2c_add_adapter, each new virtual bus/adapter created here will create
a new entry in i2c-core's master adapter list at a point that hasn't
yet been reached by i2c_add_driver's scan, and thus i2c_add_driver
will cause yet another call to our attach_adapter for each of these
new bus/adapters.
Each call to our attach_adapter() causes another call to i2c_detect,
which would detect the mux all over again if an actual probe was made.
This is prevented in two ways. The first line of defense is
i2c_check_addr() which is called by both i2c-proc.c:i2c_detect()
and i2c-core.c:i2c_probe() to make sure a bus/addr pair has not already
been seen; that function now knows how to verify this on all parent
busses as well.
The second line of defense is to never put anything in the "normal_i2c"
or "normal_i2c_range" tables that would induce i2c_detect() to probe
a PCA954x address.
*/
int pca954x_detect(struct i2c_adapter *adapter, int address,
unsigned short flags, int kind)
{
int i, n;
struct i2c_client *client;
struct pca954x_data *data;
const char *type_name = "";
int err = 0;
DBG(printk(KERN_DEBUG "%s: %0lX %0x %0x %d\n",
__FUNCTION__, (unsigned long)adapter,
address, flags, kind));
/* XXX: Remove this after verifying that no more recursion
is happening!
*/
if (adapter->algo->id == I2C_ALGO_VIRT) {
printk(KERN_ERR "%s: Avoiding recursion! %0lX %0x %0x %d\n",
__FUNCTION__, (unsigned long)adapter,
address, flags, kind);
goto ERROR0;
}
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
goto ERROR0;
/* OK. For now, we presume we have a valid client. We now create the
client structure, even though we cannot fill it completely yet. */
if (!(data = kmalloc(sizeof(struct pca954x_data), GFP_KERNEL))) {
err = -ENOMEM;
goto ERROR0;
}
memset(data, 0, sizeof(struct pca954x_data));
client = &data->client;
client->addr = address;
client->data = data;
client->adapter = adapter;
client->driver = &pca954x_driver;
client->flags = 0;
/* The detection is very weak. In fact, we have no way of
* telling what kind of mux/swi is there, and the differences matter,
* so if asked to figure it out we just complain instead.
*/
if (kind < 0) {
printk(KERN_ERR "%s: Attempted ill-advised probe at addr %0x",
__FUNCTION__, address);
goto ERROR1;
}
/* Read the mux register at addr. This does two things: it verifies
that the mux is in fact present, and fetches its current
contents for possible use with a future deselect algorithm.
*/
if ((i = i2c_smbus_read_byte(client)) < 0) {
printk(KERN_WARNING
"i2c-%d: pca954x.o failed to read reg at %0x",
i2c_adapter_id(adapter), address);
goto ERROR1;
}
data->origval = i;
if (kind == any_chip) {
printk(KERN_WARNING
"i2c-%d: pca954x.o needs advice on chip type -"
" wildly guessing %0x is a PCA9540",
i2c_adapter_id(adapter), address);
kind = pca9540; /* Make "any" default to PCA9540 */
}
/* Look up in table */
for (i = sizeof(pca954x_chipdefs)/sizeof(pca954x_chipdefs[0]);
--i >= 0;) {
if (pca954x_chipdefs[i].type == kind)
break;
}
if (i < 0) {
printk(KERN_ERR "%s: Internal error: unknown kind (%d)",
__FUNCTION__, kind);
goto ERROR1;
}
data->type = kind;
type_name = pca954x_chipdefs[i].type_name;
data->pcanum = pca954x_chipdefs[i].pcanum;
data->nchans = pca954x_chipdefs[i].nchans;
data->muxtype = pca954x_chipdefs[i].muxtype;
data->nchmask = (1 << data->nchans) - 1;
/* Now that we know the mux/swi type, we can analyze the
pca954x_selmask parameter, if one was specified.
XXX: Not yet. Still uncertain whether there is a good way
to initialize selmask and still continue to detect other chips
correctly on the right busses, or whether the original reg
setting (origval) should be retained in case BIOS depends on it.
*/
/* Fill in remaining client fields and put it into the global list */
snprintf(client->name, sizeof(client->name),
"PCA%d I2C %d-chan %s chip",
data->pcanum, data->nchans,
(data->muxtype == pca954x_ismux ? "mux" : "switch"));
client->id = pca954x_id++;
init_MUTEX(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(client)))
goto ERROR1;
/* Register new /proc directory entries with module sensors */
if ((i = i2c_register_entry(client, type_name,
pca954x_dir_table_template,
THIS_MODULE)) < 0) {
err = i;
goto ERROR2;
}
data->sysctl_id = i;
/* Initialize the PCA954X chip.
For now, force unselected. This had better happen BEFORE any
other sensors modules are loaded, or their chip addresses will
stop working if they were actually behind the mux chip.
*/
pca954x_set_selmask(client, 0);
/* Now create virtual busses and adapters for them */
#ifdef I2C_REQUIRE_ARBITRATION
/* This doesn't actually do anything yet; kept as a
placeholder from the original i2c-virtual_cb.c
*/
adapter->algo->acquire_exclusive = &acquire_shared_bus;
adapter->algo->release_exclusive = &release_shared_bus;
#endif
DBG(printk(KERN_DEBUG "%s: creating %d virt busses\n",
__FUNCTION__, data->nchans));
for (i = 0, n = 0; i < data->nchans; ++i) {
struct i2c_adapter *virt;
virt = i2c_virt_create_adapter(adapter, client, i,
&pca954x_select_mux,
&pca954x_deselect_mux);
if ((data->virt_adapters[i] = virt) != NULL)
++n;
}
printk("i2c-%d: Registered %d of %d virtual busses for I2C mux %s\n",
i2c_adapter_id(adapter), n, i, type_name);
return 0;
ERROR2:
i2c_detach_client(client);
ERROR1:
kfree(data);
ERROR0:
return err;
}
static int pca954x_detach_client(struct i2c_client *client)
{
struct pca954x_data *data = (struct pca954x_data *)(client->data);
int i, err;
DBG(printk(KERN_DEBUG "%s: %0lX\n",
__FUNCTION__, (unsigned long)client));
/* Must flush everything attached to our channels!
*/
for (i = 0; i < data->nchans; ++i) {
if (data->virt_adapters[i]) {
err = i2c_virt_remove_adapter(data->virt_adapters[i]);
if (err != 0) {
printk(KERN_ERR "pca954x.o: Bus deregistration failed, "
"client not detached.\n");
return err;
}
data->virt_adapters[i] = NULL;
}
}
i2c_deregister_entry(data->sysctl_id);
if ((err = i2c_detach_client(client))) {
printk(KERN_ERR "pca954x.o: Client deregistration failed, "
"client not detached.\n");
return err;
}
kfree(client->data);
return 0;
}
/* Implement /proc "regval" - the actual contents of the chip register.
* This is READ-ONLY.
*/
void pca954x_pe_regval(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
#if 0 /* XXX: For now, always use cached value */
static void pca954x_update_client(struct i2c_client *client);
pca954x_update_client(client);
#endif
results[0] = data->regval;
*nrels_mag = 1;
}
}
/* Implement /proc "debug" - a non-zero value enables debug output, if
* the code was compiled with it. This actually applies to all mux chips,
* not just a single instance, but that's OK.
*/
void pca954x_pe_debug(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
results[0] = pca954x_debug;
*nrels_mag = 1;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
pca954x_debug = results[0];
}
}
}
/* Implement /proc "busses" - list of (virtual) adapter IDs that constitute
the busses that this chip connects to.
This is READ-ONLY.
*/
void pca954x_pe_busses(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
int i, lasti;
if ((lasti = data->nchans) > *nrels_mag)
lasti = *nrels_mag;
for (i = 0; i < lasti; ++i) {
results[i] = i2c_adapter_id(data->virt_adapters[i]);
}
*nrels_mag = lasti;
}
}
/* Implement /proc "selmask" - the value specifies the current default
* channel mask. Sub-bus 0 == bit 0.
# A value of 0 means "none"
*/
void pca954x_pe_selmask(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
results[0] = data->selmask;
*nrels_mag = 1;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
if (*nrels_mag >= 1) {
pca954x_set_selmask(client, results[0]);
}
}
}
/* Implement /proc "selbus" - the list of values is considered to be virtual
adapter IDs, as an alternative representation of the select mask.
No values at all means "none".
*/
void pca954x_pe_selbus(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
int i, nres;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
for (i = 0, nres = 0; i < data->nchans; ++i) {
if ((data->selmask & (1<<i)) && (nres < *nrels_mag)) {
results[nres] = i2c_adapter_id(data->virt_adapters[i]);
++nres;
}
}
*nrels_mag = nres;
} else if (operation == SENSORS_PROC_REAL_WRITE) {
int newmask = 0;
for (nres = 0; nres < *nrels_mag; ++nres) {
/* Ugly, but shouldn't happen very often */
for (i = 0; i < data->nchans; ++i) {
if (results[nres]
== i2c_adapter_id(data->virt_adapters[i])) {
newmask |= (1<<i);
}
}
}
pca954x_set_selmask(client, newmask);
}
}
/* Implement /proc "type" - expose details of the mux type.
Due to limitations of the sensor package conventions, we can only provide
an integer array rather than strings:
[0] = chip number: 9540 for "PCA9540", etc.
[1] = mux type: 0 for mux, 1 for switch
[2] = # channels: 2, 4, or 8
*/
void pca954x_pe_type(struct i2c_client *client, int operation,
int ctl_name, int *nrels_mag, long *results)
{
struct pca954x_data *data = client->data;
if (operation == SENSORS_PROC_REAL_INFO)
*nrels_mag = 0;
else if (operation == SENSORS_PROC_REAL_READ) {
if (*nrels_mag >= 3) {
results[0] = data->pcanum;
results[1] = data->muxtype;
results[2] = data->nchans;
*nrels_mag = 3;
}
}
}
/* Set default select mask.
Since this can be executed while something else is using the mux or
one of its sub-busses, the main consideration is making sure we don't
mess up anything in progress.
This is accomplished by locking the parent bus that the mux resides on,
and then calling our internal write function.
It would not work to just call i2c_smbus_write_byte() because
by the time that returns, the values we're trying to update
could already be wrong. This can happen if something else grabs
the bus and writes to the mux (eg for doing I/O to a sub-bus
device), leading to a race condition for updating
selmask, regval, and curmask.
It doesn't help to use an update_lock for those values because
you still have to contend with other threads trying to select
sub-busses and would encounter deadlocks.
*/
static void pca954x_set_selmask(struct i2c_client *client, int mask)
{
struct pca954x_data *data = client->data;
DBG(printk(KERN_DEBUG
"%s: Locking bus %d mux %d (%0lX) "
"for write of selmask %0x\n",
__FUNCTION__, i2c_adapter_id(client->adapter),
client->id, (unsigned long)client, mask));
down(&client->adapter->bus);
DBG(printk(KERN_DEBUG "%s: Starting write.\n", __FUNCTION__));
data->selmask = (mask & data->nchmask);
(void) pca954x_select_chan(client->adapter, client, -1);
up(&client->adapter->bus);
DBG(printk(KERN_DEBUG "%s: Unlocked mux (%0lX) for write.\n",
__FUNCTION__, (unsigned long)client));
}
#if 0 /* Not needed at the moment; see pca954x_pe_regval() */
/* Read pca954x register into cached value
*/
static void pca954x_update_client(struct i2c_client *client)
{
struct pca954x_data *data = client->data;
DBG(printk(KERN_DEBUG
"%s: Locking bus %d mux %d (%0lX) for read\n",
__FUNCTION__, i2c_adapter_id(client->adapter),
client->id, (unsigned long)client));
down(&client->adapter->bus);
DBG(printk(KERN_DEBUG "%s: Starting read.\n", __FUNCTION__));
pca954x_xfer(client->adapter, client, I2C_SMBUS_READ, &data->regval);
data->curmask = pca954x_val2mask(client, data->regval);
up(&client->adapter->bus);
DBG(printk(KERN_DEBUG "%s: Unlocked mux (%0lX) for read.\n",
__FUNCTION__, (unsigned long)client));
}
#endif
static u8 pca954x_mask2val(struct i2c_client *client, int mask)
{
struct pca954x_data *data = (struct pca954x_data *)(client->data);
int i;
mask &= data->nchmask; /* Sanitize mask */
if (mask == 0)
return 0; /* Deselecting all, turn off */
if (data->muxtype == pca954x_isswi)
return mask; /* Switch, can enable all at once! */
/* Mux, can only pick one. Select lowest chan bit */
for (i=0; (mask & (1<<i)) == 0; ++i);
return PCA954X_MUX_ENA | i;
}
static int pca954x_val2mask(struct i2c_client *client, u8 val)
{
struct pca954x_data *data = (struct pca954x_data *)(client->data);
if (data->muxtype == pca954x_isswi)
return val & data->nchmask; /* Switch val is == mask */
/* Mux - mask off low bits to get 0-based chan # as bit shift */
if (val & PCA954X_MUX_ENA)
return 1 << (val & (data->nchans-1));
return 0;
}
/*****************************************************************************
* All of the following functions should only be called after the parent
* adapter bus semaphore (adapter->bus) has been acquired, so we cannot
* call the high-level i2c_smbus_write_byte() function -- it will deadlock
* attempting to acquire the same bus lock. Instead do directly what it
* would do, by means of pca954x_xfer(). Sigh!
*
* These functions are allowed to block.
*
*****************************************************************************/
static int pca954x_select_mux(struct i2c_adapter *adap,
struct i2c_mux_ctrl *mux)
{
return pca954x_select_chan(adap, mux->client, mux->value);
}
static int pca954x_deselect_mux(struct i2c_adapter *adap,
struct i2c_mux_ctrl *mux)
{
return pca954x_select_chan(adap, mux->client, -1);
}
/* Select channel.
* PCA954x I2C-controlled bus mux/switch chips mainly differ in
* whether the chip is a mux or a switch:
* Mux: value addressed, only 1 bus can be selected.
* Swi: bitmask selected, any/all busses can be selected.
*/
static int pca954x_select_chan(struct i2c_adapter *adap,
struct i2c_client *client,
unsigned long chan)
{
struct pca954x_data *data = (struct pca954x_data *)(client->data);
int maskbit;
u8 newregval;
int ret;
if (chan == -1) { /* Op: clever deselect (to selmask) */
if ((data->muxtype == pca954x_ismux)
&& (data->curmask & data->selmask)) {
/* If mux, and current chan allowed by selmask, do nothing */
newregval = data->regval; /* No change */
} else {
/* Anything else just reverts to selmask */
newregval = pca954x_mask2val(client, data->selmask);
}
} else { /* Op: select chan */
maskbit = 1 << chan;
if (maskbit & data->curmask) { /* If already selected, */
newregval = data->regval; /* no change */
} else if ((data->muxtype == pca954x_ismux)
|| !(maskbit & data->selmask)) {
newregval = pca954x_mask2val(client, maskbit);
} else
newregval = pca954x_mask2val(client, data->selmask);
}
/* Only clobber control reg when value changes, to avoid
unnecessary mux writes. For safety and initialization,
ALWAYS force the write if 0 is desired.
*/
ret = (!newregval || (data->regval != newregval));
DBG(printk(KERN_DEBUG
"%s: Mux %d: chan %d, val %02x - %s\n",
__FUNCTION__,
client->id, (int)chan, newregval,
(ret ? "writing" : "skipping" ) ));
if (ret) {
ret = pca954x_xfer(adap, client, I2C_SMBUS_WRITE, &newregval);
if (ret < 0) {
printk(KERN_ERR "%s: I2C mux %d select failed "
"(addr=%02x, val=%02x, err=%d)!\n",
__FUNCTION__, client->id,
client->addr, newregval, ret);
} else {
data->regval = newregval;
data->curmask = pca954x_val2mask(client, newregval);
}
}
return ret;
}
static int pca954x_xfer(struct i2c_adapter *adap,
struct i2c_client *client,
int read_write, u8 *val)
{
int ret;
if (adap->algo->master_xfer) {
/* Use I2C transfer */
struct i2c_msg msg;
char buf[1];
msg.addr = client->addr;
msg.flags = (read_write == I2C_SMBUS_READ ? I2C_M_RD : 0);
msg.len = 1;
buf[0] = *val;
msg.buf = buf;
ret = adap->algo->master_xfer(adap, &msg, 1);
if (!ret && (read_write == I2C_SMBUS_READ))
*val = buf[0];
} else if (adap->algo->smbus_xfer) {
/* Use SMBus transfer */
union i2c_smbus_data data;
ret = adap->algo->smbus_xfer(adap,
client->addr,
client->flags & I2C_M_TEN,
read_write,
*val,
I2C_SMBUS_BYTE, &data);
if (!ret && (read_write == I2C_SMBUS_READ))
*val = data.byte;
} else {
printk(KERN_ERR "%s: no smbus_xfer or master_xfer for "
"adapter %0lX, client %0lX\n",
__FUNCTION__, (unsigned long)adap, (unsigned long)client);
ret = -1;
}
return ret;
}
/* ------------------ */
MODULE_AUTHOR("Ken Harrenstien <klh@google.com>");
MODULE_DESCRIPTION("PCA954X I2C mux/switch driver");
module_init(pca954x_init);
module_exit(pca954x_exit);
-71
View File
@@ -1,71 +0,0 @@
/*
* i2c-virtual.h - Header file for the 'virtual i2c' adapter driver.
*
* Copyright (c) 2004 Google, Inc.
*
* Based on:
* i2c-virtual.h from Brian Kuschak <bkuschak@yahoo.com>
*
* 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.
*/
#ifndef __I2C_VIRTUAL_H
#define __I2C_VIRTUAL_H
#include <linux/i2c.h>
struct i2c_mux_ctrl {
struct i2c_client *client; /* The mux chip/device */
unsigned long addr;
unsigned long value; /* Channel # */
/* fn which enables the mux */
int (*select)(struct i2c_adapter *adap, struct i2c_mux_ctrl *mux);
/* fn which disables the mux */
int (*deselect)(struct i2c_adapter *adap, struct i2c_mux_ctrl *mux);
};
/* This has to be exposed, since the code which assigns the callbacks must
use it.
*/
struct i2c_virt_priv {
struct i2c_adapter *parent_adap; /* pointer to parent adapter */
struct i2c_mux_ctrl mux; /* MUX settings for this adapter */
};
static inline struct i2c_adapter *i2c_virt_parent(struct i2c_adapter *adap)
{
if (adap->algo && (adap->algo->id == I2C_ALGO_VIRT)) {
return ((struct i2c_virt_priv *)(adap->data))->parent_adap;
}
return NULL;
}
/*
* Called to create a 'virtual' i2c bus which represents a multiplexed bus
* segment. The client and mux_val are passed to the select and deselect
* callback functions to perform hardware-specific mux control.
*/
struct i2c_adapter *i2c_virt_create_adapter(struct i2c_adapter *parent_adap,
struct i2c_client *client,
unsigned long mux_val,
void *select_cb,
void *deselect_cb);
int i2c_virt_remove_adapter(struct i2c_adapter *adap);
#endif /* __I2C_VIRTUAL_H */
+2 -3
View File
@@ -7,10 +7,9 @@ Description: Hardware health monitoring package for Linux. It consists of
of a library to allow applications to read this data more
easily, and of an example program to pretty print this data.
LM78, LM79, LM80, W83781D and GL518SM sensor chips are
supported, among others. PIIX4 and VIA chipset SMBus
implementations are supported, as well as many I2C busses.
supported, among others.
Version 2 is a complete non-compatible modular rewrite of this
package, with full I2C and SMBus support.
package.
Keywords: kernel module SMBus sensors LM78 health
Author: frodol@dds.nl (Frodo Looijaard)
phil@netroedge.com (Philip Edelbrock)