mirror of
https://github.com/lm-sensors/lm-sensors.git
synced 2026-11-08 16:11:26 +00:00
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:
1 parent
adb328263c
commit
b170e0be99
56 files changed
+10
-12595
No files matched your search
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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 /\\.# | \
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
@@ -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!
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
@@ -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/.
|
||||
@@ -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
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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__ */
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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");
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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
@@ -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)
|
||||
|
||||
Reference in new issue
Block a user