#!/usr/bin/perl # # sensors-detect - Detect PCI bus and chips # Copyright (C) 1998 - 2002 Frodo Looijaard # Copyright (C) 2004 - 2008 Jean Delvare # # 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., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA. # # TODO: Better handling of chips with several addresses # A Perl wizard really ought to look upon this; the PCI and I2C stuff should # each be put in a separate file, using modules and packages. That is beyond # me. require 5.004; use strict; use Fcntl; use POSIX; use File::Basename; # We will call modprobe, which typically lives in either /sbin, # /usr/sbin or /usr/local/bin. So make sure these are all in the PATH. foreach ('/usr/sbin', '/usr/local/sbin', '/sbin') { $ENV{PATH} = "$_:".$ENV{PATH} unless $ENV{PATH} =~ m/(^|:)$_\/?(:|$)/; } ######################### # CONSTANT DECLARATIONS # ######################### use vars qw(@pci_adapters @chip_ids $i2c_addresses_to_scan @superio_ids @cpu_ids $revision); $revision = '$Revision: 5249 $ ($Date: 2008-05-11 22:56:25 +0200 (Sun, 11 May 2008) $)'; $revision =~ s/\$\w+: (.*?) \$/$1/g; $revision =~ s/ \([^()]*\)//; # This is the list of SMBus or I2C adapters we recognize by their PCI # signature. This is an easy and fast way to determine which SMBus or I2C # adapters should be present. # Each entry must have a vendid (Vendor ID), devid (Device ID) and # procid (string as appears in /proc/pci; see linux/driver/pci, # either pci.c or oldproc.c). If no driver is written yet, set the # driver (Driver Name) field to "to-be-written". # The match (Match Description) field should contain a regular expression # matching the adapter name as it would appear in /proc/bus/i2c or /sys. @pci_adapters = ( { vendid => 0x8086, devid => 0x7113, procid => "Intel 82371AB PIIX4 ACPI", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x8086, devid => 0x7603, procid => "Intel 82372FB PIIX5 ACPI", driver => "to-be-tested", } , { vendid => 0x8086, devid => 0x719b, procid => "Intel 82443MX Mobile", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x8086, devid => 0x2413, procid => "Intel 82801AA ICH", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x2423, procid => "Intel 82801AB ICH0", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x2443, procid => "Intel 82801BA ICH2", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x2483, procid => "Intel 82801CA/CAM ICH3", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x24C3, procid => "Intel 82801DB ICH4", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x24D3, procid => "Intel 82801EB ICH5", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x25A4, procid => "Intel 6300ESB", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x269B, procid => "Intel Enterprise Southbridge - ESB2", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, }, { vendid => 0x8086, devid => 0x266A, procid => "Intel 82801FB ICH6", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x27DA, procid => "Intel 82801G ICH7", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, } , { vendid => 0x8086, devid => 0x283E, procid => "Intel 82801H ICH8", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, }, { vendid => 0x8086, devid => 0x2930, procid => "Intel ICH9", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, }, { vendid => 0x8086, devid => 0x5032, procid => "Intel Tolapai", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, }, { vendid => 0x8086, devid => 0x3A30, procid => "Intel ICH10", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, }, { vendid => 0x8086, devid => 0x3A60, procid => "Intel ICH10", driver => "i2c-i801", match => qr/^SMBus I801 adapter at [0-9a-f]{4}/, }, { vendid => 0x1106, devid => 0x3040, procid => "VIA Technologies VT82C586B Apollo ACPI", driver => "i2c-via", match => qr/^VIA i2c/, } , { vendid => 0x1106, devid => 0x3050, procid => "VIA Technologies VT82C596 Apollo ACPI", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x3051, procid => "VIA Technologies VT82C596B ACPI", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x3057, procid => "VIA Technologies VT82C686 Apollo ACPI", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x3074, procid => "VIA Technologies VT8233 VLink South Bridge", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x3147, procid => "VIA Technologies VT8233A South Bridge", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x3177, procid => "VIA Technologies VT8233A/8235 South Bridge", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x3227, procid => "VIA Technologies VT8237 South Bridge", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x3337, procid => "VIA Technologies VT8237A South Bridge", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x8235, procid => "VIA Technologies VT8231 South Bridge", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x3287, procid => "VIA Technologies VT8251 South Bridge", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, } , { vendid => 0x1106, devid => 0x8324, procid => "VIA Technologies CX700 South Bridge", driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at/, }, { vendid => 0x1039, devid => 0x5597, procid => "Silicon Integrated Systems SIS5581/5582/5597/5598 (To be written - Do not use 5595 drivers)", driver => "to-be-written", } , { vendid => 0x1039, devid => 0x5598, procid => "Silicon Integrated Systems SIS5598 (To be written - Do not use 5595 drivers)", driver => "to-be-written", } , { vendid => 0x1039, devid => 0x0540, procid => "Silicon Integrated Systems SIS540 (To be written - Do not use 5595 drivers)", driver => "to-be-written", } , { vendid => 0x1039, devid => 0x0630, procid => "Silicon Integrated Systems SIS630", driver => "i2c-sis630", match => qr/^SMBus SIS630 adapter at [0-9a-f]{4}/, } , { vendid => 0x1039, devid => 0x0730, procid => "Silicon Integrated Systems SIS730", driver => "i2c-sis630", match => qr/^SMBus SIS630 adapter at [0-9a-f]{4}/, } , # # Both Ali chips below have same PCI ID. Can't be helped. Only one should load. # { vendid => 0x10b9, devid => 0x7101, procid => "Acer Labs 1533/1543", driver => "i2c-ali15x3", match => qr/^SMBus ALI15X3 adapter at/, }, { vendid => 0x10b9, devid => 0x7101, procid => "Acer Labs 1535", driver => "i2c-ali1535", match => qr/^SMBus ALI1535 adapter at/, }, { vendid => 0x10b9, devid => 0x1563, procid => "Acer Labs 1563", driver => "i2c-ali1563", match => qr/^SMBus ALi 1563 Adapter @/, }, { vendid => 0x1022, devid => 0x740b, procid => "AMD-756 Athlon ACPI", driver => "i2c-amd756", match => qr/^SMBus AMD756 adapter at [0-9a-f]{4}/, }, { vendid => 0x1022, devid => 0x7413, procid => "AMD-766 Athlon ACPI", driver => "i2c-amd756", match => qr/^SMBus AMD766 adapter at [0-9a-f]{4}/, }, { vendid => 0x1022, devid => 0x7443, procid => "AMD-768 System Management", driver => "i2c-amd756", match => qr/^SMBus AMD768 adapter at [0-9a-f]{4}/, }, { vendid => 0x1022, devid => 0x746b, procid => "AMD-8111 ACPI", driver => "i2c-amd756", match => qr/^SMBus AMD8111 adapter at [0-9a-f]{4}/, }, { vendid => 0x1022, devid => 0x746a, procid => "AMD-8111 SMBus 2.0", driver => "i2c-amd8111", match => qr/^SMBus2 AMD8111 adapter at [0-9a-f]{4}/, }, { vendid => 0x10de, devid => 0x01b4, procid => "nVidia nForce SMBus", driver => "i2c-amd756", match => qr/^SMBus nVidia nForce adapter at [0-9a-f]{4}/, } , { vendid => 0x10de, devid => 0x0064, procid => "nVidia Corporation nForce2 SMBus (MCP)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x0084, procid => "nVidia Corporation nForce2 Ultra 400 SMBus (MCP)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x00D4, procid => "nVidia Corporation nForce3 Pro150 SMBus (MCP)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x00E4, procid => "nVidia Corporation nForce3 250Gb SMBus (MCP)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x0052, procid => "nVidia Corporation nForce4 SMBus (MCP)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x0034, procid => "nVidia Corporation nForce4 SMBus (MCP-04)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x0264, procid => "nVidia Corporation nForce4 SMBus (MCP51)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x0368, procid => "nVidia Corporation nForce4 SMBus (MCP55)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x03eb, procid => "nVidia Corporation nForce4 SMBus (MCP61)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x10de, devid => 0x0446, procid => "nVidia Corporation nForce4 SMBus (MCP65)", driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at /, }, { vendid => 0x1166, devid => 0x0200, procid => "ServerWorks OSB4 South Bridge", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x1055, devid => 0x9463, procid => "SMSC Victory66 South Bridge", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x1166, devid => 0x0201, procid => "ServerWorks CSB5 South Bridge", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x1166, devid => 0x0203, procid => "ServerWorks CSB6 South Bridge", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x1166, devid => 0x0205, procid => "ServerWorks HT-1000 South Bridge", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, }, { vendid => 0x1002, devid => 0x4353, procid => "ATI Technologies Inc ATI SMBus", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x1002, devid => 0x4363, procid => "ATI Technologies Inc ATI SMBus", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x1002, devid => 0x4372, procid => "ATI Technologies Inc IXP SB400 SMBus Controller", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x1002, devid => 0x4385, procid => "ATI Technologies Inc SB600 SMBus", driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at /, } , { vendid => 0x100B, devid => 0x0500, procid => "SCx200 Bridge", driver => "scx200_acb", match => qr/^(NatSemi SCx200 ACCESS\.bus|SCx200 ACB\d+) /, }, { vendid => 0x100B, devid => 0x0510, procid => "SC1100 Bridge", driver => "scx200_acb", match => qr/^(NatSemi SCx200 ACCESS\.bus|SCx200 ACB\d+) /, }, { vendid => 0x100B, devid => 0x002B, procid => "CS5535 ISA bridge", driver => "scx200_acb", match => qr/^CS5535 ACB\d+ /, }, { vendid => 0x1022, devid => 0x2090, procid => "CS5536 [Geode companion] ISA", driver => "scx200_acb", match => qr/^CS553[56] ACB\d+ /, }, ); # The following entries used to appear directly in @pci_adapters. # Because of the tendency of SiS chipsets to have their real PCI # IDs obscured, we have to qualify these with a custom detection # routine before we add them to the @pci_adapters list. # use vars qw(@pci_adapters_sis5595 @pci_adapters_sis645 @pci_adapters_sis96x); @pci_adapters_sis5595 = ( { vendid => 0x1039, devid => 0x0008, procid => "Silicon Integrated Systems SIS5595", driver => "i2c-sis5595", match => qr/^SMBus SIS5595 adapter at [0-9a-f]{4}/, } , ); @pci_adapters_sis645 = ( { vendid => 0x1039, devid => 0x0008, procid => "Silicon Integrated Systems SIS5595", driver => "i2c-sis645", match => qr/^SiS645 SMBus adapter at [0-9a-f]{4}/, } , { vendid => 0x1039, devid => 0x0016, procid => "Silicon Integrated Systems SMBus Controller", driver => "i2c-sis645", match => qr/^SiS645 SMBus adapter at 0x[0-9a-f]{4}/, } , { vendid => 0x1039, devid => 0x0018, procid => "Silicon Integrated Systems 85C503/5513 (LPC Bridge)", driver => "i2c-sis645", match => qr/^SiS645 SMBus adapter at 0x[0-9a-f]{4}/, } , ); @pci_adapters_sis96x = ( { vendid => 0x1039, devid => 0x0016, procid => "Silicon Integrated Systems SMBus Controller", driver => "i2c-sis96x", match => qr/^SiS96x SMBus adapter at 0x[0-9a-f]{4}/, } , ); # This is a list of all recognized chips. # Each entry must have the following fields: # name: The full chip name # driver: The driver name (without .o extension). Put in exactly # "to-be-written" if it is not yet available. Put in exactly # "not-a-sensor" if it is not a hardware monitoring chip. # Put in exactly "use-isa-instead" if no i2c driver will be written. # i2c_addrs (optional): For I2C chips, the range of valid I2C addresses to # probe. Recommend avoiding 0x69 because of clock chips. # i2c_detect (optional): For I2C chips, the function to call to detect # this chip. The function should take two parameters: an open file # descriptor to access the bus, and the I2C address to probe. # isa_addrs (optional): For ISA chips, the range of valid port addresses to # probe. # isa_detect (optional): For ISA chips, the function to call to detect # this chip. The function should take one parameter: the ISA address # to probe. # alias_detect (optional): For chips which can be both on the ISA and the # I2C bus, a function which detectes whether two entries are the same. # The function should take three parameters: The ISA address, the # I2C bus number, and the I2C address. @chip_ids = ( { name => "Myson MTP008", driver => "mtp008", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { mtp008_detect(@_); }, } , { name => "National Semiconductor LM78", driver => "lm78", i2c_addrs => [0x28..0x2f], i2c_detect => sub { lm78_detect(0, @_); }, isa_addrs => [0x290], isa_detect => sub { lm78_isa_detect(0, @_); }, alias_detect => sub { lm78_alias_detect(0, @_); }, } , { name => "National Semiconductor LM78-J", driver => "lm78", i2c_addrs => [0x28..0x2f], i2c_detect => sub { lm78_detect(1, @_); }, isa_addrs => [0x290], isa_detect => sub { lm78_isa_detect(1, @_); }, alias_detect => sub { lm78_alias_detect(1, @_); }, } , { name => "National Semiconductor LM79", driver => "lm78", i2c_addrs => [0x28..0x2f], i2c_detect => sub { lm78_detect(2, @_); }, isa_addrs => [0x290], isa_detect => sub { lm78_isa_detect(2, @_); }, alias_detect => sub { lm78_alias_detect(2, @_); }, } , { name => "National Semiconductor LM75", driver => "lm75", i2c_addrs => [0x48..0x4f], i2c_detect => sub { lm75_detect(0, @_); }, }, { name => "Dallas Semiconductor DS75", driver => "lm75", i2c_addrs => [0x48..0x4f], i2c_detect => sub { lm75_detect(1, @_); }, }, { name => "National Semiconductor LM77", driver => "lm77", i2c_addrs => [0x48..0x4b], i2c_detect => sub { lm77_detect(@_); }, }, { name => "National Semiconductor LM80", driver => "lm80", i2c_addrs => [0x28..0x2f], i2c_detect => sub { lm80_detect(@_); }, }, { name => "National Semiconductor LM85 or LM96000", driver => "lm85", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { lm85_detect(0x01, @_); }, }, { name => "Analog Devices ADM1027, ADT7460 or ADT7463", driver => "lm85", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { lm85_detect(0x41, @_); }, }, { name => "SMSC EMC6D100, EMC6D101 or EMC6D102", driver => "lm85", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { lm85_detect(0x5c, @_); }, }, { name => "Analog Devices ADT7462", driver => "to-be-written", # The datasheet says addresses 0x5C and 0x58, but I guess these are # left-aligned values i2c_addrs => [0x2c, 0x2e], i2c_detect => sub { adt7467_detect(2, @_); }, }, { name => "Analog Devices ADT7466", driver => "to-be-written", i2c_addrs => [0x4c], i2c_detect => sub { adt7467_detect(3, @_); }, }, { name => "Analog Devices ADT7467 or ADT7468", driver => "to-be-written", i2c_addrs => [0x2e], i2c_detect => sub { adt7467_detect(0, @_); }, }, { name => "Analog Devices ADT7470", driver => "adt7470", i2c_addrs => [0x2c, 0x2e, 0x2f], i2c_detect => sub { adt7467_detect(4, @_); }, }, { name => "Analog Devices ADT7473", driver => "to-be-written", i2c_addrs => [0x2e], i2c_detect => sub { adt7473_detect(0, @_); }, }, { name => "Analog Devices ADT7475", driver => "to-be-written", i2c_addrs => [0x2e], i2c_detect => sub { adt7473_detect(1, @_); }, }, { name => "Analog Devices ADT7476", driver => "to-be-written", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { adt7467_detect(1, @_); }, }, { name => "Andigilog aSC7511", driver => "to-be-written", i2c_addrs => [0x4c], i2c_detect => sub { andigilog_aSC7511_detect(0, @_); }, }, { name => "Andigilog aSC7512", driver => "to-be-written", i2c_addrs => [0x58], i2c_detect => sub { andigilog_detect(0, @_); }, }, { name => "Andigilog aSC7611", driver => "to-be-written", i2c_addrs => [0x2c, 0x2d, 0x2e], i2c_detect => sub { andigilog_detect(1, @_); }, }, { name => "Andigilog aSC7621", driver => "to-be-written", i2c_addrs => [0x2c, 0x2d, 0x2e], i2c_detect => sub { andigilog_detect(2, @_); }, }, { name => "National Semiconductor LM87", driver => "lm87", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { lm87_detect(@_); }, }, { name => "National Semiconductor LM93", driver => "lm93", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { lm93_detect(@_); }, }, { name => "Winbond W83781D", driver => "w83781d", i2c_detect => sub { w83781d_detect(0, @_); }, i2c_addrs => [0x28..0x2f], isa_addrs => [0x290], isa_detect => sub { w83781d_isa_detect(0, @_); }, alias_detect => sub { w83781d_alias_detect(0, @_); }, } , { name => "Winbond W83782D", driver => "w83781d", i2c_addrs => [0x28..0x2f], i2c_detect => sub { w83781d_detect(1, @_); }, isa_addrs => [0x290], isa_detect => sub { w83781d_isa_detect(1, @_); }, alias_detect => sub { w83781d_alias_detect(1, @_); }, } , { name => "Winbond W83783S", driver => "w83781d", i2c_addrs => [0x2d], i2c_detect => sub { w83781d_detect(2, @_); }, } , { name => "Winbond W83792D", driver => "w83792d", i2c_addrs => [0x2c..0x2f], i2c_detect => sub { w83781d_detect(8, @_); }, }, { name => "Winbond W83793R/G", driver => "w83793", i2c_addrs => [0x2c..0x2f], i2c_detect => sub { w83793_detect(0, @_); }, }, { name => "Winbond W83791SD", driver => "not-a-sensor", i2c_addrs => [0x2c..0x2f], i2c_detect => sub { w83791sd_detect(@_); }, }, { name => "Winbond W83627HF", driver => "use-isa-instead", i2c_addrs => [0x28..0x2f], i2c_detect => sub { w83781d_detect(3, @_); }, }, { name => "Winbond W83627EHF", driver => "use-isa-instead", i2c_addrs => [0x28..0x2f], i2c_detect => sub { w83781d_detect(9, @_); }, }, { name => "Winbond W83627DHG", driver => "use-isa-instead", i2c_addrs => [0x28..0x2f], i2c_detect => sub { w83781d_detect(10, @_); }, }, { name => "Asus AS99127F (rev.1)", driver => "w83781d", i2c_addrs => [0x28..0x2f], i2c_detect => sub { w83781d_detect(4, @_); }, } , { name => "Asus AS99127F (rev.2)", driver => "w83781d", i2c_addrs => [0x28..0x2f], i2c_detect => sub { w83781d_detect(5, @_); }, } , { name => "Asus ASB100 Bach", driver => "asb100", i2c_addrs => [0x28..0x2f], i2c_detect => sub { w83781d_detect(6, @_); }, } , { name => "Asus ASM58 Mozart-2", driver => "to-be-written", i2c_addrs => [0x77], i2c_detect => sub { mozart_detect(0, @_); }, } , { name => "Asus AS2K129R Mozart-2", driver => "to-be-written", i2c_addrs => [0x77], i2c_detect => sub { mozart_detect(1, @_); }, } , { name => "Asus Mozart-2", driver => "to-be-written", i2c_addrs => [0x77], i2c_detect => sub { mozart_detect(2, @_); }, } , { name => "Winbond W83L784R/AR/G", driver => "to-be-written", i2c_addrs => [0x2d], i2c_detect => sub { w83l784r_detect(0, @_); }, } , { name => "Winbond W83L785R/G", driver => "to-be-written", i2c_addrs => [0x2d], i2c_detect => sub { w83l784r_detect(1, @_); }, } , { name => "Winbond W83L786NR/NG/R/G", driver => "to-be-written", i2c_addrs => [0x2e, 0x2f], i2c_detect => sub { w83l784r_detect(2, @_); }, }, { name => "Winbond W83L785TS-S", driver => "w83l785ts", i2c_addrs => [0x2e], i2c_detect => sub { w83l785ts_detect(0, @_); }, } , { name => "Genesys Logic GL518SM Revision 0x00", driver => "gl518sm", i2c_addrs => [0x2c, 0x2d], i2c_detect => sub { gl518sm_detect(0, @_); }, }, { name => "Genesys Logic GL518SM Revision 0x80", driver => "gl518sm", i2c_addrs => [0x2c, 0x2d], i2c_detect => sub { gl518sm_detect(1, @_); }, }, { name => "Genesys Logic GL520SM", driver => "gl520sm", i2c_addrs => [0x2c, 0x2d], i2c_detect => sub { gl520sm_detect(@_); }, }, { name => "Genesys Logic GL525SM", driver => "Unwritten (GL525SM)", i2c_addrs => [0x2d], i2c_detect => sub { gl525sm_detect(@_); }, }, { name => "Analog Devices ADM9240", driver => "adm9240", i2c_addrs => [0x2c..0x2f], i2c_detect => sub { adm9240_detect(0, @_); }, }, { name => "Dallas Semiconductor DS1621/DS1631", driver => "ds1621", i2c_addrs => [0x48..0x4f], i2c_detect => sub { ds1621_detect(@_); }, } , { name => "Dallas Semiconductor DS1780", driver => "adm9240", i2c_addrs => [0x2c..0x2f], i2c_detect => sub { adm9240_detect(1, @_); }, }, { name => "National Semiconductor LM81", driver => "adm9240", i2c_addrs => [0x2c..0x2f], i2c_detect => sub { adm9240_detect(2, @_); }, }, { name => "Analog Devices ADM1026", driver => "adm1026", i2c_addrs => [0x2c,0x2d,0x2e], i2c_detect => sub { adm1026_detect(0, @_); }, }, { name => "Analog Devices ADM1025", driver => "adm1025", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { adm1025_detect(0, @_); }, }, { name => "Philips NE1619", driver => "adm1025", i2c_addrs => [0x2c..0x2d], i2c_detect => sub { adm1025_detect(1, @_); }, }, { name => "Analog Devices ADM1021", driver => "adm1021", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { adm1021_detect(0, @_); }, }, { name => "Analog Devices ADM1021A/ADM1023", driver => "adm1021", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { adm1021_detect(1, @_); }, }, { name => "Maxim MAX1617", driver => "adm1021", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { adm1021_detect(2, @_); }, }, { name => "Maxim MAX1617A", driver => "adm1021", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { adm1021_detect(3, @_); }, }, { name => "Maxim MAX1668", driver => "max1668", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { max1668_detect(0, @_); }, }, { name => "Maxim MAX1805", driver => "max1668", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { max1668_detect(1, @_); }, }, { name => "Maxim MAX1989", driver => "max1668", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { max1668_detect(2, @_); }, }, { name => "Maxim MAX6650/MAX6651", driver => "max6650", i2c_addrs => [0x1b,0x1f,0x48,0x4b], i2c_detect => sub { max6650_detect(0, @_); }, }, { name => "Maxim MAX6655/MAX6656", driver => "max6655", i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e], i2c_detect => sub { max6655_detect(0, @_); }, }, { name => "TI THMC10", driver => "adm1021", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { adm1021_detect(4, @_); }, }, { name => "National Semiconductor LM84", driver => "adm1021", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { adm1021_detect(5, @_); }, }, { name => "Genesys Logic GL523SM", driver => "adm1021", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { adm1021_detect(6, @_); }, }, { name => "Onsemi MC1066", driver => "adm1021", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { adm1021_detect(7, @_); }, }, { name => "Maxim MAX1619", driver => "max1619", i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e], i2c_detect => sub { max1619_detect(0, @_); }, }, { name => "National Semiconductor LM82/LM83", driver => "lm83", i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e], i2c_detect => sub { lm83_detect(0, @_); }, }, { name => "National Semiconductor LM90", driver => "lm90", i2c_addrs => [0x4c], i2c_detect => sub { lm90_detect(0, @_); }, }, { name => "National Semiconductor LM89/LM99", driver => "lm90", i2c_addrs => [0x4c..0x4d], i2c_detect => sub { lm90_detect(1, @_); }, }, { name => "National Semiconductor LM86", driver => "lm90", i2c_addrs => [0x4c], i2c_detect => sub { lm90_detect(2, @_); }, }, { name => "Analog Devices ADM1032", driver => "lm90", i2c_addrs => [0x4c..0x4d], i2c_detect => sub { lm90_detect(3, @_); }, }, { name => "Maxim MAX6657/MAX6658/MAX6659", driver => "lm90", i2c_addrs => [0x4c], i2c_detect => sub { lm90_detect(4, @_); }, }, { name => "Maxim MAX6659", driver => "lm90", i2c_addrs => [0x4d..0x4e], # 0x4c is handled above i2c_detect => sub { lm90_detect(4, @_); }, }, { name => "Maxim MAX6648/MAX6692", driver => "to-be-written", i2c_addrs => [0x4c], i2c_detect => sub { lm90_detect(6, @_); }, }, { name => "Maxim MAX6680/MAX6681", driver => "lm90", i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e], i2c_detect => sub { lm90_detect(7, @_); }, }, { name => "Winbond W83L771W/G", driver => "lm90", i2c_addrs => [0x4c], i2c_detect => sub { lm90_detect(8, @_); }, }, { name => "Texas Instruments TMP401", driver => "tmp401", i2c_addrs => [0x4c], i2c_detect => sub { lm90_detect(9, @_); }, }, { name => "National Semiconductor LM63", driver => "lm63", i2c_addrs => [0x4c], i2c_detect => sub { lm63_detect(1, @_); }, }, { name => "National Semiconductor LM64", driver => "to-be-written", # lm63 i2c_addrs => [0x18, 0x4e], i2c_detect => sub { lm63_detect(3, @_); }, }, { name => "Fintek F75363SG", driver => "lm63", # Not yet i2c_addrs => [0x4c], i2c_detect => sub { lm63_detect(2, @_); }, }, { name => "National Semiconductor LM92", driver => "lm92", i2c_addrs => [0x48..0x4b], i2c_detect => sub { lm92_detect(0, @_); }, }, { name => "National Semiconductor LM76", driver => "lm92", i2c_addrs => [0x48..0x4b], i2c_detect => sub { lm92_detect(1, @_); }, }, { name => "Maxim MAX6633/MAX6634/MAX6635", driver => "lm92", i2c_addrs => [0x48..0x4f], # The MAX6633 can also use 0x40-0x47 but we # don't want to probe these addresses, it's # dangerous. i2c_detect => sub { lm92_detect(2, @_); }, }, { name => "Analog Devices ADT7461", driver => "lm90", i2c_addrs => [0x4c..0x4d], i2c_detect => sub { lm90_detect(5, @_); }, }, { name => "Analog Devices ADM1029", driver => "adm1029", i2c_addrs => [0x28..0x2f], i2c_detect => sub { adm1029_detect(0, @_); }, }, { name => "Analog Devices ADM1030", driver => "adm1031", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { adm1031_detect(0, @_); }, }, { name => "Analog Devices ADM1031", driver => "adm1031", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { adm1031_detect(1, @_); }, }, { name => "Analog Devices ADM1033", driver => "to-be-written", i2c_addrs => [0x50..0x53], i2c_detect => sub { adm1034_detect(0, @_); }, }, { name => "Analog Devices ADM1034", driver => "to-be-written", i2c_addrs => [0x50..0x53], i2c_detect => sub { adm1034_detect(1, @_); }, }, { name => "Analog Devices ADM1022", driver => "thmc50", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { adm1022_detect(0, @_); }, }, { name => "Texas Instruments THMC50", driver => "thmc50", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { adm1022_detect(1, @_); }, }, { name => "Analog Devices ADM1028", driver => "thmc50", i2c_addrs => [0x2e], i2c_detect => sub { adm1022_detect(2, @_); }, }, { name => "VIA VT1211 (I2C)", driver => "use-isa-instead", i2c_addrs => [0x2d], i2c_detect => sub { vt1211_i2c_detect(0, @_); }, }, { name => "ITE IT8712F", driver => "it87", i2c_addrs => [0x28..0x2f], i2c_detect => sub { ite_detect(0, @_); }, }, { name => "ITE IT8201R/IT8203R/IT8206R/IT8266R", driver => "not-a-sensor", i2c_addrs => [0x4e], i2c_detect => sub { ite_overclock_detect(@_); }, }, { name => "SPD EEPROM", driver => "not-a-sensor", # Can also live at 0x54-0x57, but we don't care: we only check # for SPD and EDID EEPROMs because some hardware monitoring chips # can live at 0x50-0x53. i2c_addrs => [0x50..0x53], i2c_detect => sub { eeprom_detect(@_); }, }, { name => "EDID EEPROM", driver => "not-a-sensor", i2c_addrs => [0x50..0x53], i2c_detect => sub { ddcmonitor_detect(@_); }, }, { name => "FSC Poseidon II", driver => "to-be-written", i2c_addrs => [0x73], i2c_detect => sub { fscpos_detect(@_); }, }, { name => "FSC Heimdal", driver => "fschmd", i2c_addrs => [0x73], i2c_detect => sub { fschmd_detect(@_); }, }, { name => "FSC Heracles", driver => "fschmd", i2c_addrs => [0x73], i2c_detect => sub { fschrc_detect(@_); }, }, { name => "ALi M5879", driver => "to-be-written", i2c_addrs => [0x2c..0x2d], i2c_detect => sub { m5879_detect(@_); }, }, { name => "SMSC LPC47M15x/192/292/997", driver => "smsc47m192", i2c_addrs => [0x2c..0x2d], i2c_detect => sub { smsc47m192_detect(@_); }, }, { name => "SMSC DME1737", driver => "dme1737", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { dme1737_detect(1, @_); }, }, { name => "SMSC SCH5027D-NW", driver => "dme1737", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { dme1737_detect(2, @_); }, }, { name => "Fintek F75111R/RG/N (GPIO)", driver => "not-a-sensor", i2c_addrs => [0x4e], # 0x37 not probed i2c_detect => sub { fintek_detect(1, @_); }, }, { name => "Fintek F75121R/F75122R/RG (VID+GPIO)", driver => "to-be-written", i2c_addrs => [0x4e], # 0x37 not probed i2c_detect => sub { fintek_detect(2, @_); }, }, { name => "Fintek F75373S/SG", driver => "f75375s", i2c_addrs => [0x2d..0x2e], i2c_detect => sub { fintek_detect(3, @_); }, }, { name => "Fintek F75375S/SP", driver => "f75375s", i2c_addrs => [0x2d..0x2e], i2c_detect => sub { fintek_detect(4, @_); }, }, { name => "Fintek F75387SG/RG", driver => "to-be-written", i2c_addrs => [0x2d..0x2e], i2c_detect => sub { fintek_detect(5, @_); }, }, { name => "Fintek F75383S/M", driver => "to-be-written", i2c_addrs => [0x4c], i2c_detect => sub { fintek_detect(6, @_); }, }, { name => "Fintek F75384S/M", driver => "to-be-written", i2c_addrs => [0x4d], i2c_detect => sub { fintek_detect(6, @_); }, }, { name => "Fintek custom power control IC", driver => "to-be-written", i2c_addrs => [0x2f], i2c_detect => sub { fintek_detect(7, @_); }, }, { name => "Smart Battery", driver => "smartbatt", i2c_addrs => [0x0b], i2c_detect => sub { smartbatt_detect(@_); }, }, ); # Special case chip information goes here and would be included in # the chip_special_cases routine below use vars qw(@chip_kern24_ids @chip_kern26_ids @chip_oldfsc_ids @chip_fschmd_ids); @chip_kern24_ids = ( { name => "Analog Devices ADM1024", driver => "adm1024", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { adm1024_detect(0, @_); }, }, { name => "Winbond W83791D", driver => "w83781d", i2c_addrs => [0x2c..0x2f], i2c_detect => sub { w83781d_detect(7, @_); }, }, { name => "FSC Scylla", driver => "fscscy", i2c_addrs => [0x73], i2c_detect => sub { fscscy_detect(@_); }, }, { name => "IPMI BMC KCS", driver => "bmcsensors", isa_addrs => [0x0ca0], isa_detect => sub { ipmi_kcs_detect(@_); }, }, { name => "IPMI BMC SMIC", driver => "bmcsensors", isa_addrs => [0x0ca8], isa_detect => sub { ipmi_smic_detect(@_); }, }, ); @chip_kern26_ids = ( { name => "Analog Devices ADM1024", driver => "lm87", i2c_addrs => [0x2c..0x2e], i2c_detect => sub { adm1024_detect(0, @_); }, }, { name => "Winbond W83791D", driver => "w83791d", i2c_addrs => [0x2c..0x2f], i2c_detect => sub { w83781d_detect(7, @_); }, }, { name => "FSC Scylla", driver => "fschmd", i2c_addrs => [0x73], i2c_detect => sub { fscscy_detect(@_); }, }, { name => "IPMI BMC KCS", driver => "ipmisensors", isa_addrs => [0x0ca0], isa_detect => sub { ipmi_kcs_detect(@_); }, }, { name => "IPMI BMC SMIC", driver => "ipmisensors", isa_addrs => [0x0ca8], isa_detect => sub { ipmi_smic_detect(@_); }, }, ); # sigh special case for old seperate FSC drivers to new merged one mapping @chip_oldfsc_ids = ( { name => "FSC Poseidon I", driver => "fscpos", i2c_addrs => [0x73], i2c_detect => sub { fscpeg_detect(@_); }, }, { name => "FSC Hermes", driver => "fscher", i2c_addrs => [0x73], i2c_detect => sub { fscher_detect(@_); }, }, ); @chip_fschmd_ids = ( { name => "FSC Poseidon I", driver => "fschmd", i2c_addrs => [0x73], i2c_detect => sub { fscpeg_detect(@_); }, }, { name => "FSC Hermes", driver => "fschmd", i2c_addrs => [0x73], i2c_detect => sub { fscher_detect(@_); }, }, ); # This is a list of all recognized superio chips. # Each entry must have the following fields: # name: The full chip name # driver: The driver name (without .o extension). Put in # "to-be-written" if it is not yet available. # Put in "not-a-sensor" if the chip doesn't have hardware monitoring # capabilities (listing such chips here removes the need of manual # lookup when people report them). # Put in exactly "via-smbus-only" if this is a Super-I/O chip whose # hardware monitoring registers can only be accessed via the SMBus. # devid: The device ID(s) we have to match (base device) # devid_mask (optional): Bitmask to apply before checking the device ID # logdev: The logical device containing the sensors # alias_detect (optional): For chips which can be both on the ISA and the # I2C bus, a function which detectes whether two entries are the same. # The function should take three parameters: The ISA address, the # I2C bus number, and the I2C address. # Entries are grouped by family. Each family entry has the following fields: # family: The family name # guess (optional): Typical logical device address. This lets us do # generic probing if we fail to recognize the chip. # enter: The password sequence to write to the address register # chips: Array of chips # The order of families matters, because we stop as soon as one family # succeeds. So we have to list families with shorter password sequences # first. @superio_ids = ( { family => "National Semiconductor", enter => { 0x2e => [], 0x4e => [], }, chips => [ { name => "Nat. Semi. PC8374L Super IO Sensors", driver => "to-be-written", devid => 0xf1, logdev => 0x08, }, { name => "Nat. Semi. PC87351 Super IO Fan Sensors", driver => "to-be-written", devid => 0xe2, logdev => 0x08, }, { name => "Nat. Semi. PC87360 Super IO Fan Sensors", driver => "pc87360", devid => 0xe1, logdev => 0x09, }, { name => "Nat. Semi. PC87363 Super IO Fan Sensors", driver => "pc87360", devid => 0xe8, logdev => 0x09, }, { name => "Nat. Semi. PC87364 Super IO Fan Sensors", driver => "pc87360", devid => 0xe4, logdev => 0x09, }, { name => "Nat. Semi. PC87365 Super IO Fan Sensors", driver => "pc87360", devid => 0xe5, logdev => 0x09, }, { name => "Nat. Semi. PC87365 Super IO Voltage Sensors", driver => "pc87360", devid => 0xe5, logdev => 0x0d, }, { name => "Nat. Semi. PC87365 Super IO Thermal Sensors", driver => "pc87360", devid => 0xe5, logdev => 0x0e, }, { name => "Nat. Semi. PC87366 Super IO Fan Sensors", driver => "pc87360", devid => 0xe9, logdev => 0x09, }, { name => "Nat. Semi. PC87366 Super IO Voltage Sensors", driver => "pc87360", devid => 0xe9, logdev => 0x0d, }, { name => "Nat. Semi. PC87366 Super IO Thermal Sensors", driver => "pc87360", devid => 0xe9, logdev => 0x0e, }, { name => "Nat. Semi. PC87372 Super IO Fan Sensors", driver => "to-be-written", devid => 0xf0, logdev => 0x09, }, { name => "Nat. Semi. PC87373 Super IO Fan Sensors", driver => "to-be-written", devid => 0xf3, logdev => 0x09, }, { name => "Nat. Semi. PC87591 Super IO", driver => "to-be-written", devid => 0xec, logdev => 0x0f, }, { name => "Nat. Semi. PC87317 Super IO", driver => "not-a-sensor", devid => 0xd0, }, { name => "Nat. Semi. PC97317 Super IO", driver => "not-a-sensor", devid => 0xdf, }, { name => "Nat. Semi. PC8739x Super IO", driver => "not-a-sensor", devid => 0xea, }, { name => "Nat. Semi. PC8741x Super IO", driver => "not-a-sensor", devid => 0xee, }, { name => "Nat. Semi. PC87427 Super IO Fan Sensors", driver => "pc87427", devid => 0xf2, logdev => 0x09, }, { name => "Nat. Semi. PC87427 Super IO Health Sensors", driver => "to-be-written", devid => 0xf2, logdev => 0x14, }, ], }, { family => "SMSC", enter => { 0x2e => [0x55], 0x4e => [0x55], }, chips => [ { name => "SMSC DME1737 Super IO", # Hardware monitoring features are accessed on the SMBus driver => "via-smbus-only", devid => 0x78, }, { name => "SMSC DME1737 Super IO", # The DME1737 shows up twice in this list because it can return either # 0x78 or 0x77 as its device ID. # Hardware monitoring features are accessed on the SMBus driver => "via-smbus-only", devid => 0x77, }, { name => "SMSC FDC37B72x Super IO", driver => "not-a-sensor", devid => 0x4c, }, { name => "SMSC FDC37B78x Super IO", driver => "not-a-sensor", devid => 0x44, }, { name => "SMSC FDC37C672 Super IO", driver => "not-a-sensor", devid => 0x40, }, { name => "SMSC FDC37M707 Super IO", driver => "not-a-sensor", devid => 0x42, }, { name => "SMSC FDC37M81x Super IO", driver => "not-a-sensor", devid => 0x4d, }, { name => "SMSC LPC47B27x Super IO Fan Sensors", driver => "smsc47m1", devid => 0x51, logdev => 0x0a, }, { name => "SMSC LPC47B34x Super IO", driver => "not-a-sensor", devid => 0x56, }, { name => "SMSC LPC47B357/M967 Super IO", driver => "not-a-sensor", devid => 0x5d, }, { name => "SMSC LPC47B367-NC Super IO", driver => "not-a-sensor", devid => 0x6d, }, { name => "SMSC LPC47B37x Super IO Fan Sensors", driver => "to-be-written", devid => 0x52, logdev => 0x0a, }, { name => "SMSC LPC47B397-NC Super IO", driver => "smsc47b397", devid => 0x6f, logdev => 0x08, }, { name => "SMSC LPC47M10x/112/13x Super IO Fan Sensors", driver => "smsc47m1", devid => 0x59, logdev => 0x0a, }, { name => "SMSC LPC47M14x Super IO Fan Sensors", driver => "smsc47m1", devid => 0x5f, logdev => 0x0a, }, { name => "SMSC LPC47M15x/192/997 Super IO Fan Sensors", driver => "smsc47m1", devid => 0x60, logdev => 0x0a, }, { name => "SMSC LPC47M172 Super IO Fan Sensors", driver => "to-be-written", devid => 0x14, logdev => 0x0a, }, { name => "SMSC LPC47M182 Super IO Fan Sensors", driver => "to-be-written", devid => 0x74, logdev => 0x0a, }, { name => "SMSC LPC47M292 Super IO Fan Sensors", driver => "smsc47m1", devid => 0x6b, logdev => 0x0a, }, { name => "SMSC LPC47M584-NC Super IO", # No datasheet devid => 0x76, }, { name => "SMSC LPC47N252 Super IO Fan Sensors", driver => "to-be-written", devid => 0x0e, logdev => 0x09, }, { name => "SMSC LPC47S42x Super IO Fan Sensors", driver => "to-be-written", devid => 0x57, logdev => 0x0a, }, { name => "SMSC LPC47S45x Super IO Fan Sensors", driver => "to-be-written", devid => 0x62, logdev => 0x0a, }, { name => "SMSC LPC47U33x Super IO Fan Sensors", driver => "to-be-written", devid => 0x54, logdev => 0x0a, }, { name => "SMSC SCH3112 Super IO", driver => "dme1737", devid => 0x7c, logdev => 0x0a, }, { name => "SMSC SCH3114 Super IO", driver => "dme1737", devid => 0x7d, logdev => 0x0a, }, { name => "SMSC SCH3116 Super IO", driver => "dme1737", devid => 0x7f, logdev => 0x0a, }, { name => "SMSC SCH4307 Super IO Fan Sensors", driver => "to-be-written", devid => 0x90, logdev => 0x08, }, { name => "SMSC SCH5027D-NW Super IO", # Hardware monitoring features are accessed on the SMBus driver => "via-smbus-only", devid => 0x89, }, { name => "SMSC SCH5127 Super IO", driver => "dme1737", devid => 0x86, logdev => 0x0a, }, { name => "SMSC SCH5307-NS Super IO", driver => "smsc47b397", devid => 0x81, logdev => 0x08, }, { name => "SMSC SCH5317 Super IO", driver => "smsc47b397", devid => 0x85, logdev => 0x08, }, { name => "SMSC SCH5317 Super IO", # The SCH5317 shows up twice in this list because it can return either # 0x85 or 0x8c as its device ID. driver => "smsc47b397", devid => 0x8c, logdev => 0x08, }, { name => "SMSC SCH5504-NS Super IO", # No datasheet driver => "not-a-sensor", devid => 0x79, }, { name => "SMSC SCH5514D-NS Super IO", # No datasheet driver => "not-a-sensor", devid => 0x83, }, ], # Non-standard SMSC detection callback and chip list. These chips differ # from the standard ones listed above in that the device ID register # address is 0x0d instead of 0x20 (as specified by the ISA PNP spec). ns_detect => \&smsc_ns_detect_superio, ns_chips => [ { name => "SMSC FDC37C665 Super IO", driver => "not-a-sensor", devid => 0x65, }, { name => "SMSC FDC37C666 Super IO", driver => "not-a-sensor", devid => 0x66, }, { name => "SMSC FDC37C669 Super IO", driver => "not-a-sensor", devid => 0x03, }, { name => "SMSC FDC37N769 Super IO", driver => "not-a-sensor", devid => 0x28, }, { name => "SMSC LPC47N227 Super IO", driver => "not-a-sensor", devid => 0x5a, }, ], }, { family => "VIA/Winbond/Fintek", guess => 0x290, enter => { 0x2e => [0x87, 0x87], 0x4e => [0x87, 0x87], }, chips => [ { name => "VIA VT1211 Super IO Sensors", driver => "vt1211", devid => 0x3c, logdev => 0x0b, alias_detect => sub { vt1211_alias_detect(0, @_); }, }, { name => "Winbond W83627HF/F/HG/G Super IO Sensors", driver => "w83627hf", devid => 0x52, logdev => 0x0b, alias_detect => sub { w83781d_alias_detect(3, @_); }, }, { name => "Winbond W83627THF/THG Super IO Sensors", driver => "w83627hf", devid => 0x82, logdev => 0x0b, }, { name => "Winbond W83637HF/HG Super IO Sensors", driver => "w83627hf", devid => 0x70, logdev => 0x0b, }, { name => "Winbond W83687THF Super IO Sensors", driver => "w83627hf", devid => 0x85, logdev => 0x0b, }, { name => "Winbond W83697HF/F/HG Super IO Sensors", driver => "w83627hf", devid => 0x60, logdev => 0x0b, }, { name => "Winbond W83697SF/UF/UG Super IO PWM", driver => "to-be-written", devid => 0x68, logdev => 0x0b, }, { name => "Winbond W83627EHF/EF/EHG/EG Super IO Sensors", driver => "w83627ehf", # W83627EHF datasheet says 0x886x but 0x8853 was seen, thus the # broader mask. W83627EHG was seen with ID 0x8863. devid => 0x8840, devid_mask => 0xFFC0, logdev => 0x0b, alias_detect => sub { w83781d_alias_detect(9, @_); }, }, { name => "Winbond W83627DHG Super IO Sensors", driver => "w83627ehf", devid => 0xA020, devid_mask => 0xFFF0, logdev => 0x0b, alias_detect => sub { w83781d_alias_detect(10, @_); }, }, { name => "Winbond W83L517D Super IO", driver => "not-a-sensor", devid => 0x61, }, { name => "Fintek F71805F/FG Super IO Sensors", driver => "f71805f", devid => 0x0406, logdev => 0x04, }, { name => "Fintek F71862FG Super IO Sensors", driver => "to-be-written", devid => 0x0601, logdev => 0x04, }, { name => "Fintek F71806FG/F71872FG Super IO Sensors", driver => "f71805f", devid => 0x0341, logdev => 0x04, }, { name => "Fintek F71858DG Super IO Sensors", driver => "to-be-written", devid => 0x0507, logdev => 0x02, }, { name => "Fintek F71882FG/F71883FG Super IO Sensors", driver => "f71882fg", devid => 0x0541, logdev => 0x04, }, { name => "Fintek F81216D Super IO", driver => "not-a-sensor", devid => 0x0208, }, { name => "Fintek F81218D Super IO", driver => "not-a-sensor", devid => 0x0206, }, { name => "Asus F8000 Super IO", driver => "f8000", devid => 0x0581, logdev => 0x04, }, { # Shouldn't be in this family, but seems to be still. name => "ITE IT8708F Super IO", driver => "not-a-sensor", devid => 0x8708, }, ], }, { family => "ITE", guess => 0x290, enter => { 0x2e => [0x87, 0x01, 0x55, 0x55], 0x4e => [0x87, 0x01, 0x55, 0xaa], }, chips => [ { name => "ITE IT8702F Super IO Sensors", driver => "to-be-written", devid => 0x8702, logdev => 0x04, }, { name => "ITE IT8705F Super IO Sensors", driver => "it87", devid => 0x8705, logdev => 0x04, }, { name => "ITE IT8712F Super IO Sensors", driver => "it87", devid => 0x8712, logdev => 0x04, alias_detect => sub { ite_alias_detect(0, @_); }, }, { name => "ITE IT8716F Super IO Sensors", driver => "it87", devid => 0x8716, logdev => 0x04, }, { name => "ITE IT8718F Super IO Sensors", driver => "it87", devid => 0x8718, logdev => 0x04, }, { name => "ITE IT8726F Super IO Sensors", driver => "it87", devid => 0x8726, logdev => 0x04, }, ], }, ); # Drivers for CPU embedded sensors # Each entry must have the following fields: # name: The CPU family name # driver: The driver name. Put "to-be-written" if no driver is available. # detect: Detection callback function. No parameter will be passed to # this function, it must use global lists of PCI devices, CPU, # etc. It must return a confidence value, undef if no supported # CPU is found. @cpu_ids = ( { name => "Silicon Integrated Systems SIS5595", driver => "sis5595", detect => sub { sis5595_pci_detect(); }, }, { name => "VIA VT82C686 Integrated Sensors", driver => "via686a", detect => sub { via686a_pci_detect(); }, }, { name => "VIA VT8231 Integrated Sensors", driver => "vt8231", detect => sub { via8231_pci_detect(); }, }, { name => "AMD K8 thermal sensors", driver => "k8temp", detect => sub { k8temp_pci_detect(); }, }, { name => "AMD K10 thermal sensors", driver => "to-be-written", detect => sub { k10temp_pci_detect(); }, }, { name => "Intel Core family thermal sensor", driver => "coretemp", detect => sub { coretemp_detect(); }, }, { name => "Intel AMB FB-DIMM thermal sensor", driver => "to-be-written", detect => sub { intel_amb_detect(); }, }, ); ####################### # AUXILIARY FUNCTIONS # ####################### sub swap_bytes { return (($_[0] & 0xff00) >> 8) + (($_[0] & 0x00ff) << 8) } # $_[0] is the sought value # @_[1..] is the list to seek in # Returns: 0 on failure, 1 if found. # Note: Every use of this sub probably indicates the use of the wrong # datastructure sub contains { my $sought = shift; foreach (@_) { return 1 if $sought eq $_; } return 0; } # Address can be decimal or hexadecimal sub valid_address { my $value = shift; if ($value !~ m/^(0x[0-9a-f]+|[0-9]+)$/i) { print "$value is not a valid address, sorry.\n"; exit -1; } $value = oct($value) if $value =~ /^0x/i; return $value; } sub parse_not_to_scan { my ($min,$max,$to_parse) = @_; my @ranges = split /\s*,\s*/, $to_parse; my @res; my $range; foreach $range (@ranges) { my ($start, $end) = split /\s*-\s*/, $range; $start = valid_address($start); if (defined $end) { $end = valid_address($end); if ($end <= $start) { print "$start-$end is not a valid range, sorry.\n"; exit -1; } $start = $min if $start < $min; $end = $max if $end > $max; push @res, ($start..$end); } else { push @res, $start if $start >= $min and $start <= $max; } } return sort { $a <=> $b } @res; } # @_[0]: Reference to list 1 # @_[1]: Reference to list 2 # Result: 0 if they have no elements in common, 1 if they have # Elements must be numeric. sub any_list_match { my ($list1,$list2) = @_; my ($el1,$el2); foreach $el1 (@$list1) { foreach $el2 (@$list2) { return 1 if $el1 == $el2; } } return 0; } ################### # I/O port access # ################### sub initialize_ioports { sysopen (IOPORTS, "/dev/port", O_RDWR) or die "/dev/port: $!\n"; binmode IOPORTS; } sub close_ioports { close (IOPORTS) or print "Warning: $!\n"; } # $_[0]: port to read # Returns: -1 on failure, read value on success. sub inb { my ($res,$nrchars); sysseek IOPORTS, $_[0], 0 or return -1; $nrchars = sysread IOPORTS, $res, 1; return -1 if not defined $nrchars or $nrchars != 1; $res = unpack "C",$res ; return $res; } # $_[0]: port to write # $_[1]: value to write # Returns: -1 on failure, 0 on success. sub outb { my $towrite = pack "C", $_[1]; sysseek IOPORTS, $_[0], 0 or return -1; my $nrchars = syswrite IOPORTS, $towrite, 1; return -1 if not defined $nrchars or $nrchars != 1; return 0; } # $_[0]: Address register # $_[1]: Data register # $_[2]: Register to read # Returns: read value sub isa_read_byte { outb $_[0],$_[2]; return inb $_[1]; } # $_[0]: Address register # $_[1]: Data register # $_[2]: Register to write # $_[3}: Value to write # Returns: nothing sub isa_write_byte { outb $_[0],$_[2]; outb $_[1],$_[3]; } ################# # AUTODETECTION # ################# use vars qw($modules_conf $dev_i2c $sysfs_root); sub initialize_conf { my $use_devfs = 0; open(local *INPUTFILE, "/proc/mounts") or die "Can't access /proc/mounts!"; local $_; while () { if (m@^\w+ /dev devfs @) { $use_devfs = 1; $dev_i2c = '/dev/i2c/'; } if (m@^\S+ (/\w+) sysfs @) { $sysfs_root = $1; } } close INPUTFILE; my $use_udev = 0; if (open(*INPUTFILE, '/etc/udev/udev.conf')) { while () { if (m/^\s*udev_db\s*=\s*\"([^"]*)\"/ || m/^\s*udev_db\s*=\s*(\S+)/) { if (-e $1) { $use_udev = 1; $dev_i2c = '/dev/i2c-'; } last; } } close INPUTFILE; } if (!$use_udev) { # Try some known default udev db locations, just in case if (-e '/dev/.udev.tdb' || -e '/dev/.udev' || -e '/dev/.udevdb') { $use_udev = 1; $dev_i2c = '/dev/i2c-'; } } if (kernel_version_at_least(2, 6, 0)) { $modules_conf = '/etc/modprobe.conf'; } else { $modules_conf = '/etc/modules.conf'; } if (!($use_devfs || $use_udev)) { if (! -c '/dev/i2c-0' && -x '/sbin/MAKEDEV') { system("/sbin/MAKEDEV i2c"); } if (! -c '/dev/i2c-0' && -x '/dev/MAKEDEV') { system("/dev/MAKEDEV i2c"); } if (-c '/dev/i2c-0') { $dev_i2c = '/dev/i2c-'; } else { # default print "No i2c device files found.\n"; exit -1; } } } # [0] -> VERSION # [1] -> PATCHLEVEL # [2] -> SUBLEVEL # [3] -> EXTRAVERSION # use vars qw(@kernel_version $kernel_arch); sub initialize_kernel_version { `uname -r` =~ /(\d+)\.(\d+)\.(\d+)(.*)/; @kernel_version = ($1, $2, $3, $4); chomp($kernel_arch = `uname -m`); } sub kernel_version_at_least { my ($vers, $plvl, $slvl) = @_; return 1 if ($kernel_version[0] > $vers || ($kernel_version[0] == $vers && ($kernel_version[1] > $plvl || ($kernel_version[1] == $plvl && ($kernel_version[2] >= $slvl))))); return 0; } # @cpu is a list of reference to hashes, one hash per CPU. # Each entry has the following keys: vendor_id, cpu family, model, # model name and stepping, directly taken from /proc/cpuinfo. use vars qw(@cpu); sub initialize_cpu_list { open(local *INPUTFILE, "/proc/cpuinfo") or die "Can't access /proc/cpuinfo!"; local $_; my $entry; while () { if (m/^processor\s*:\s*(\d+)/) { push @cpu, $entry if scalar keys(%{$entry}); # Previous entry $entry = {}; # New entry next; } if (m/^(vendor_id|cpu family|model|model name|stepping)\s*:\s*(.+)$/) { my $k = $1; my $v = $2; $v =~ s/\s+/ /g; # Merge multiple spaces $v =~ s/ $//; # Trim trailing space $entry->{$k} = $v; next; } } push @cpu, $entry if scalar keys(%{$entry}); # Last entry close INPUTFILE; } # @i2c_adapters is a list of references to hashes, one hash per I2C/SMBus # adapter present on the system. Each entry has the following keys: name # (directly taken from either /proc/bus/i2c or /sys/class/i2c-adapter) and # driver. use vars qw(@i2c_adapters); sub initialize_i2c_adapters_list { my $entry; local $_; if (defined $sysfs_root) { my $class_dir = "${sysfs_root}/class/i2c-adapter"; opendir(local *ADAPTERS, $class_dir) or return; while (defined($_ = readdir(ADAPTERS))) { next unless m/^i2c-(\d+)$/; $entry = {}; # New entry $entry->{'name'} = sysfs_device_attribute("${class_dir}/i2c-$1", "name") || sysfs_device_attribute("${class_dir}/i2c-$1/device", "name"); next if $entry->{'name'} eq "ISA main adapter"; $entry->{'driver'} = find_adapter_driver($entry->{'name'}); $i2c_adapters[$1] = $entry; } closedir(ADAPTERS); } else { open(local *INPUTFILE, "/proc/bus/i2c") or return; while () { my ($nr, $type, $name) = /^i2c-(\d+)\s+(\S+)\s+(.*?) *(\t|$)/; next if ($type eq "dummy" || $type eq "isa"); $entry = {}; # New entry $entry->{'name'} = $name; $entry->{'driver'} = find_adapter_driver($name); $i2c_adapters[$nr] = $entry; } close(INPUTFILE); } } ########### # MODULES # ########### use vars qw(%modules_list %modules_supported); sub initialize_modules_list { open(local *INPUTFILE, "/proc/modules") or return; local $_; while () { tr/_/-/; $modules_list{$1} = 1 if m/^(\S*)/; } } sub initialize_modules_supported { foreach my $chip (@chip_ids) { $modules_supported{$chip->{driver}}++; } } ################# # SYSFS HELPERS # ################# # From a sysfs device path, return the driver name, or undef sub sysfs_device_driver($) { my $device = shift; my $link = readlink("$device/driver"); return unless defined $link; return basename($link); } # From a sysfs device path and an attribute name, return the attribute # value, or undef sub sysfs_device_attribute($$) { my ($device, $attr) = @_; my $value; open(local *FILE, "$device/$attr") or return; return unless defined($value = ); close(FILE); chomp($value); return $value; } ############## # PCI ACCESS # ############## use vars qw(%pci_list); # This function returns a list of hashes. Each hash has some PCI information: # 'domain', 'bus', 'slot' and 'func' uniquely identify a PCI device in a # computer; 'vendid' and 'devid' uniquely identify a type of device. # 'class' lets us spot unknown SMBus adapters. # This function is used when sysfs is available (Linux 2.6). sub read_sys_dev_pci($) { my $devices = shift; my ($dev, @pci_list); opendir(local *DEVICES, "$devices") or die "$devices: $!"; while (defined($dev = readdir(DEVICES))) { my %record; next unless $dev =~ m/^(?:([\da-f]+):)?([\da-f]+):([\da-f]+)\.([\da-f]+)$/; $record{domain} = hex $1; $record{bus} = hex $2; $record{slot} = hex $3; $record{func} = hex $4; $record{vendid} = oct sysfs_device_attribute("$devices/$dev", "vendor"); $record{devid} = oct sysfs_device_attribute("$devices/$dev", "device"); $record{class} = (oct sysfs_device_attribute("$devices/$dev", "class")) >> 8; push @pci_list, \%record; } return \@pci_list; } # This function returns a list of hashes. Each hash has some PCI information: # 'bus', 'slot' and 'func' uniquely identify a PCI device in a computer; # 'vendid' and 'devid' uniquely identify a type of device. # This function is used when sysfs is not available (Linux 2.4). sub read_proc_dev_pci { my ($dfn, $vend, @pci_list); open(local *INPUTFILE, "/proc/bus/pci/devices") or die "/proc/bus/pci/devices: $!"; local $_; while () { my %record; ($dfn, $vend) = map { hex } (split) [0..1]; $record{bus} = $dfn >> 8; $record{slot} = ($dfn & 0xf8) >> 3; $record{func} = $dfn & 0x07; $record{vendid} = $vend >> 16; $record{devid} = $vend & 0xffff; push @pci_list, \%record; } return \@pci_list; } sub initialize_proc_pci { my $pci_list; if (defined $sysfs_root) { $pci_list = read_sys_dev_pci("$sysfs_root/bus/pci/devices"); } else { $pci_list = read_proc_dev_pci(); } # Note that we lose duplicate devices at this point, but we don't # really care. What matters to us is which unique devices are present, # not how many of each. %pci_list = map { sprintf("%04x:%04x", $_->{vendid}, $_->{devid}) => $_ } @{$pci_list}; } ##################### # ADAPTER DETECTION # ##################### sub adapter_pci_detection_sis_96x { my $driver=""; # first, determine which driver if any... if (kernel_version_at_least(2,6,0)) { if (exists $pci_list{"1039:0016"}) { $driver = "i2c-sis96x"; } elsif (exists $pci_list{"1039:0008"}) { $driver = "i2c-sis5595"; } } elsif (kernel_version_at_least(2,4,0)) { if (exists $pci_list{"1039:0008"}) { if ((exists $pci_list{"1039:0645"}) || (exists $pci_list{"1039:0646"}) || (exists $pci_list{"1039:0648"}) || (exists $pci_list{"1039:0650"}) || (exists $pci_list{"1039:0651"}) || (exists $pci_list{"1039:0655"}) || (exists $pci_list{"1039:0661"}) || (exists $pci_list{"1039:0735"}) || (exists $pci_list{"1039:0745"}) || (exists $pci_list{"1039:0746"})) { $driver = "i2c-sis645"; } else { $driver = "i2c-sis5595"; } } elsif ((exists $pci_list{"1039:0016"}) || (exists $pci_list{"1039:0018"})) { $driver = "i2c-sis645"; } } # then, add the appropriate entries to @pci_adapters if ($driver eq "i2c-sis5595") { push @pci_adapters, @pci_adapters_sis5595; } elsif ($driver eq "i2c-sis645") { push @pci_adapters, @pci_adapters_sis645; } elsif ($driver eq "i2c-sis96x") { push @pci_adapters, @pci_adapters_sis96x; } } # Build and return a PCI device's bus ID sub pci_busid($) { my $device = shift; my $busid; $busid = sprintf("\%02x:\%02x.\%x", $device->{bus}, $device->{slot}, $device->{func}); $busid = sprintf("\%04x:", $device->{domain}) . $busid if defined $device->{domain}; return $busid; } sub adapter_pci_detection { my ($key, $device, $try, @res, %smbus); print "Probing for PCI bus adapters...\n"; # Custom detection routine for some SiS chipsets adapter_pci_detection_sis_96x(); # Build a list of detected SMBus devices foreach $key (keys %pci_list) { $device = $pci_list{$key}; $smbus{$key}++ if exists $device->{class} && $device->{class} == 0x0c05; # SMBus } # Loop over the known I2C/SMBus adapters foreach $try (@pci_adapters) { $key = sprintf("%04x:%04x", $try->{vendid}, $try->{devid}); if (exists $pci_list{$key}) { $device = $pci_list{$key}; if ($try->{driver} =~ m/^to-be-/) { printf "No known driver for device \%s: \%s\n", pci_busid($device), $try->{procid}; if ($try->{driver} eq "to-be-tested") { print "\nWe are currently looking for testers for this adapter!\n". "Please check http://www.lm-sensors.org/wiki/Devices\n". "and/or contact us if you want to help.\n\n"; print "Continue... "; ; print "\n"; } } else { printf "Use driver `\%s' for device \%s: \%s\n", $try->{driver}, pci_busid($device), $try->{procid}; push @res, $try->{driver}; } # Delete from detected SMBus device list delete $smbus{$key}; } } # Now see if there are unknown SMBus devices left foreach $key (keys %smbus) { $device = $pci_list{$key}; printf "Found unknown SMBus adapter \%04x:\%04x at \%s.\n", $device->{vendid}, $device->{devid}, pci_busid($device); } if (! @res) { print "Sorry, no supported PCI bus adapters found.\n"; } return @res; } # $_[0]: Adapter description as found in /proc/bus/i2c or sysfs sub find_adapter_driver { my $adapter; for $adapter (@pci_adapters) { return $adapter->{driver} if (exists $adapter->{match} && $_[0] =~ $adapter->{match}); } return "UNKNOWN"; } ############################# # I2C AND SMBUS /DEV ACCESS # ############################# # This should really go into a separate module/package. # These are copied from use constant IOCTL_I2C_SLAVE => 0x0703; use constant IOCTL_I2C_FUNCS => 0x0705; use constant IOCTL_I2C_SMBUS => 0x0720; use constant SMBUS_READ => 1; use constant SMBUS_WRITE => 0; use constant SMBUS_QUICK => 0; use constant SMBUS_BYTE => 1; use constant SMBUS_BYTE_DATA => 2; use constant SMBUS_WORD_DATA => 3; use constant I2C_FUNC_SMBUS_QUICK => 0x00010000; use constant I2C_FUNC_SMBUS_READ_BYTE => 0x00020000; # Get the i2c adapter's functionalities # $_[0]: Reference to an opened filehandle # Returns: -1 on failure, functionality bitfield on success. sub i2c_get_funcs($) { my $file = shift; my $funcs = pack "L", 0; # Allocate space ioctl $file, IOCTL_I2C_FUNCS, $funcs or return -1; $funcs = unpack "L", $funcs; return $funcs; } # Select the device to communicate with through its address. # $_[0]: Reference to an opened filehandle # $_[1]: Address to select # Returns: 0 on failure, 1 on success. sub i2c_set_slave_addr { my ($file,$addr) = @_; $addr += 0; # Make sure it's a number not a string ioctl $file, IOCTL_I2C_SLAVE, $addr or return 0; return 1; } # i2c_smbus_access is based upon the corresponding C function (see # ). You should not need to call this directly. # Exact calling conventions are intricate; read i2c-dev.c if you really need # to know. # $_[0]: Reference to an opened filehandle # $_[1]: SMBUS_READ for reading, SMBUS_WRITE for writing # $_[2]: Command (usually register number) # $_[3]: Transaction kind (SMBUS_BYTE, SMBUS_BYTE_DATA, etc.) # $_[4]: Reference to an array used for input/output of data # Returns: 0 on failure, 1 on success. # Note that we need to get back to Integer boundaries through the 'x2' # in the pack. This is very compiler-dependent; I wish there was some other # way to do this. sub i2c_smbus_access { my ($file,$read_write,$command,$size,$data) = @_; my $data_array = pack "C32", @$data; my $ioctl_data = pack "C2x2Ip", ($read_write,$command,$size,$data_array); ioctl $file, IOCTL_I2C_SMBUS, $ioctl_data or return 0; @{$_[4]} = unpack "C32",$data_array; return 1; } # $_[0]: Reference to an opened filehandle # $_[1]: Either 0 or 1 # Returns: -1 on failure, the 0 on success. sub i2c_smbus_write_quick { my ($file,$value) = @_; my @data; i2c_smbus_access $file, $value, 0, SMBUS_QUICK, \@data or return -1; return 0; } # $_[0]: Reference to an opened filehandle # Returns: -1 on failure, the read byte on success. sub i2c_smbus_read_byte { my ($file) = @_; my @data; i2c_smbus_access $file, SMBUS_READ, 0, SMBUS_BYTE, \@data or return -1; return $data[0]; } # $_[0]: Reference to an opened filehandle # $_[1]: Command byte (usually register number) # Returns: -1 on failure, the read byte on success. sub i2c_smbus_read_byte_data { my ($file,$command) = @_; my @data; i2c_smbus_access $file, SMBUS_READ, $command, SMBUS_BYTE_DATA, \@data or return -1; return $data[0]; } # $_[0]: Reference to an opened filehandle # $_[1]: Command byte (usually register number) # Returns: -1 on failure, the read word on success. # Use this function with care, some devices don't like word reads, # so you should do as much of the detection as possible using byte reads, # and only start using word reads when there is a good chance that # the detection will succeed. # Note: some devices use the wrong endiannes; use swap_bytes to correct for # this. sub i2c_smbus_read_word_data { my ($file,$command) = @_; my @data; i2c_smbus_access $file, SMBUS_READ, $command, SMBUS_WORD_DATA, \@data or return -1; return $data[0] + 256 * $data[1]; } # $_[0]: Reference to an opened filehandle # $_[1]: Address # $_[2]: Functionalities of this i2c adapter # Returns: 1 on successful probing, 0 else. # This function is meant to prevent AT24RF08 corruption and write-only # chips locks. This is done by choosing the best probing method depending # on the address range. sub i2c_probe($$$) { my ($file, $addr, $funcs) = @_; my $data = []; if (($addr >= 0x50 && $addr <= 0x5F) || ($addr >= 0x30 && $addr <= 0x37)) { # This covers all EEPROMs we know of, including page protection addresses. # Note that some page protection addresses will not reveal themselves with # this, because they ack on write only, but this is probably better since # some EEPROMs write-protect themselves permanently on almost any write to # their page protection address. return 0 unless ($funcs & I2C_FUNC_SMBUS_READ_BYTE); return i2c_smbus_access($file, SMBUS_READ, 0, SMBUS_BYTE, $data); } else { return 0 unless ($funcs & I2C_FUNC_SMBUS_QUICK); return i2c_smbus_access($file, SMBUS_WRITE, 0, SMBUS_QUICK, $data); } } # $_[0]: Reference to an opened file handle # Returns: 1 if the device is safe to access, 0 else. # This function is meant to prevent access to 1-register-only devices, # which are designed to be accessed with SMBus receive byte and SMBus send # byte transactions (i.e. short reads and short writes) and treat SMBus # read byte as a real write followed by a read. The device detection # routines would write random values to the chip with possibly very nasty # results for the hardware. Note that this function won't catch all such # chips, as it assumes that reads and writes relate to the same register, # but that's the best we can do. sub i2c_safety_check { my ($file) = @_; my $data; # First we receive a byte from the chip, and remember it. $data = i2c_smbus_read_byte($file); return 1 if ($data < 0); # We receive a byte again; very likely to be the same for # 1-register-only devices. return 1 if (i2c_smbus_read_byte($file) != $data); # Then we try a standard byte read, with a register offset equal to # the byte we received; we should receive the same byte value in return. return 1 if (i2c_smbus_read_byte_data($file, $data) != $data); # Then we try a standard byte read, with a slightly different register # offset; we should again receive the same byte value in return. return 1 if (i2c_smbus_read_byte_data($file, $data ^ 1) != ($data ^ 1)); # Apprently this is a 1-register-only device, restore the original register # value and leave it alone. i2c_smbus_read_byte_data($file, $data); return 0; } #################### # ADAPTER SCANNING # #################### use vars qw(@chips_detected); # We will build a complicated structure @chips_detected here, being: # A list of # references to hashes # with field 'driver', being a string with the driver name for this chip; # with field 'detected' # being a reference to a list of # references to hashes of type 'detect_data'; # with field 'misdetected' # being a reference to a list of # references to hashes of type 'detect_data' # Type detect_data: # A hash # with field 'i2c_adap' containing an adapter string as appearing # in /proc/bus/i2c (if this is an I2C detection) # with field 'i2c_devnr', contianing the /dev/i2c-* number of this # adapter (if this is an I2C detection) # with field 'i2c_driver', containing the driver name for this adapter # (if this is an I2C detection) # with field 'i2c_addr', containing the I2C address of the detection; # (if this is an I2C detection) # with field 'i2c_sub_addrs', containing a reference to a list of # other I2C addresses (if this is an I2C detection) # with field 'isa_addr' containing the ISA address this chip is on # (if this is an ISA detection) # with field 'conf', containing the confidence level of this detection # with field 'chipname', containing the chip name # This adds a detection to the above structure. We do no alias detection # here; so you should do ISA detections *after* all I2C detections. # Not all possibilities of i2c_addr and i2c_sub_addrs are exhausted. # In all normal cases, it should be all right. # $_[0]: chip driver # $_[1]: reference to data hash # Returns: Nothing sub add_i2c_to_chips_detected { my ($chipdriver,$datahash) = @_; my ($i,$new_detected_ref,$new_misdetected_ref,$detected_ref,$misdetected_ref, $main_entry,$detected_entry,$put_in_detected,@hash_addrs,@entry_addrs, $do_not_add); # First determine where the hash has to be added. for ($i = 0; $i < @chips_detected; $i++) { last if ($chips_detected[$i]->{driver} eq $chipdriver); } if ($i == @chips_detected) { push @chips_detected, { driver => $chipdriver, detected => [], misdetected => [] }; } $new_detected_ref = $chips_detected[$i]->{detected}; $new_misdetected_ref = $chips_detected[$i]->{misdetected}; # Find out whether our new entry should go into the detected or the # misdetected list. We compare all i2c addresses; if at least one matches, # but our conf value is lower, we assume this is a misdetect. @hash_addrs = ($datahash->{i2c_addr}); push @hash_addrs, @{$datahash->{i2c_sub_addrs}} if exists $datahash->{i2c_sub_addrs}; $put_in_detected = 1; $do_not_add = 0; FIND_LOOP: foreach $main_entry (@chips_detected) { foreach $detected_entry (@{$main_entry->{detected}}) { @entry_addrs = ($detected_entry->{i2c_addr}); push @entry_addrs, @{$detected_entry->{i2c_sub_addrs}} if exists $detected_entry->{i2c_sub_addrs}; if ($detected_entry->{i2c_devnr} == $datahash->{i2c_devnr} and any_list_match \@entry_addrs, \@hash_addrs) { if ($detected_entry->{conf} >= $datahash->{conf}) { $put_in_detected = 0; } if ($chipdriver eq $main_entry->{driver}) { $do_not_add = 1; } last FIND_LOOP; } } } if ($put_in_detected) { # Here, we move all entries from detected to misdetected which # match at least in one main or sub address. This may not be the # best idea to do, as it may remove detections without replacing # them with second-best ones. Too bad. # (Khali 2003-09-13) If the driver is the same, the "misdetected" # entry is simply deleted; failing to do so cause the configuration # lines generated later to look very confusing (the driver will # be told to ignore valid addresses). @hash_addrs = ($datahash->{i2c_addr}); push @hash_addrs, @{$datahash->{i2c_sub_addrs}} if exists $datahash->{i2c_sub_addrs}; foreach $main_entry (@chips_detected) { $detected_ref = $main_entry->{detected}; $misdetected_ref = $main_entry->{misdetected}; for ($i = @$detected_ref-1; $i >=0; $i--) { @entry_addrs = ($detected_ref->[$i]->{i2c_addr}); push @entry_addrs, @{$detected_ref->[$i]->{i2c_sub_addrs}} if exists $detected_ref->[$i]->{i2c_sub_addrs}; if ($detected_ref->[$i]->{i2c_devnr} == $datahash->{i2c_devnr} and any_list_match \@entry_addrs, \@hash_addrs) { push @$misdetected_ref,$detected_ref->[$i] unless $chipdriver eq $main_entry->{driver}; splice @$detected_ref, $i, 1; } } } # Now add the new entry to detected push @$new_detected_ref, $datahash; } else { # No hard work here push @$new_misdetected_ref, $datahash unless $do_not_add; } } # This adds a detection to the above structure. We also do alias detection # here; so you should do ISA detections *after* all I2C detections. # $_[0]: alias detection function # $_[1]: chip driver # $_[2]: reference to data hash # Returns: 0 if it is not an alias, datahash reference if it is. sub add_isa_to_chips_detected { my ($alias_detect,$chipdriver,$datahash) = @_; my ($i,$new_detected_ref,$new_misdetected_ref,$detected_ref,$misdetected_ref, $main_entry,$isalias); # First determine where the hash has to be added. $isalias=0; for ($i = 0; $i < @chips_detected; $i++) { last if ($chips_detected[$i]->{driver} eq $chipdriver); } if ($i == @chips_detected) { push @chips_detected, { driver => $chipdriver, detected => [], misdetected => [] }; } $new_detected_ref = $chips_detected[$i]->{detected}; $new_misdetected_ref = $chips_detected[$i]->{misdetected}; # Now, we are looking for aliases. An alias can only be the same chiptype. # If an alias is found in the misdetected list, we add the new information # and terminate this function. If it is found in the detected list, we # still have to check whether another chip has claimed this ISA address. # So we remove the old entry from the detected list and put it in datahash. # Misdetected alias detection: for ($i = 0; $i < @$new_misdetected_ref; $i++) { if (exists $new_misdetected_ref->[$i]->{i2c_addr} and not exists $new_misdetected_ref->[$i]->{isa_addr} and defined $alias_detect and $new_misdetected_ref->[$i]->{chipname} eq $datahash->{chipname}) { open(local *FILE, "$dev_i2c$new_misdetected_ref->[$i]->{i2c_devnr}") or print("Can't open $dev_i2c$new_misdetected_ref->[$i]->{i2c_devnr}?!?\n"), next; binmode FILE; i2c_set_slave_addr \*FILE,$new_misdetected_ref->[$i]->{i2c_addr} or print("Can't set I2C address for ", "$dev_i2c$new_misdetected_ref->[$i]->{i2c_devnr}?!?\n"), next; if (&$alias_detect ($datahash->{isa_addr},\*FILE, $new_misdetected_ref->[$i]->{i2c_addr})) { $new_misdetected_ref->[$i]->{isa_addr} = $datahash->{isa_addr}; return $new_misdetected_ref->[$i]; } } } # Detected alias detection: for ($i = 0; $i < @$new_detected_ref; $i++) { if (exists $new_detected_ref->[$i]->{i2c_addr} and not exists $new_detected_ref->[$i]->{isa_addr} and defined $alias_detect and $new_detected_ref->[$i]->{chipname} eq $datahash->{chipname}) { open(local *FILE, "$dev_i2c$new_detected_ref->[$i]->{i2c_devnr}") or print("Can't open $dev_i2c$new_detected_ref->[$i]->{i2c_devnr}?!?\n"), next; binmode FILE; i2c_set_slave_addr \*FILE,$new_detected_ref->[$i]->{i2c_addr} or print("Can't set I2C address for ", "$dev_i2c$new_detected_ref->[$i]->{i2c_devnr}?!?\n"), next; if (&$alias_detect ($datahash->{isa_addr},\*FILE, $new_detected_ref->[$i]->{i2c_addr})) { $new_detected_ref->[$i]->{isa_addr} = $datahash->{isa_addr}; ($datahash) = splice (@$new_detected_ref, $i, 1); $isalias=1; last; } } } # Find out whether our new entry should go into the detected or the # misdetected list. We only compare main isa_addr here, of course. # (Khali 2004-05-12) If the driver is the same, the "misdetected" # entry is simply deleted; same we do for I2C chips. foreach $main_entry (@chips_detected) { $detected_ref = $main_entry->{detected}; $misdetected_ref = $main_entry->{misdetected}; for ($i = 0; $i < @{$main_entry->{detected}}; $i++) { if (exists $detected_ref->[$i]->{isa_addr} and exists $datahash->{isa_addr} and $detected_ref->[$i]->{isa_addr} == $datahash->{isa_addr}) { if ($detected_ref->[$i]->{conf} >= $datahash->{conf}) { push @$new_misdetected_ref, $datahash unless $main_entry->{driver} eq $chipdriver; } else { push @$misdetected_ref,$detected_ref->[$i] unless $main_entry->{driver} eq $chipdriver; splice @$detected_ref, $i,1; push @$new_detected_ref, $datahash; } if ($isalias) { return $datahash; } else { return 0; } } } } # Not found? OK, put it in the detected list push @$new_detected_ref, $datahash; if ($isalias) { return $datahash; } else { return 0; } } # From the list of known I2C/SMBus devices, build a list of I2C addresses # which are worth probing. There's no point in probing an address for which # we don't know a single device, and probing some addresses has caused # random trouble in the past. sub i2c_addresses_to_scan() { my @used; my @addresses; my $addr; foreach my $chip (@chip_ids) { next unless defined $chip->{'i2c_addrs'}; next if $chip->{'driver'} eq 'not-a-sensor'; foreach $addr (@{$chip->{'i2c_addrs'}}) { $used[$addr]++; } } for ($addr = 0x03; $addr <= 0x77; $addr++) { push @addresses, $addr if $used[$addr]; } return \@addresses; } # $_[0]: The number of the adapter to scan # $_[1]: The name of the adapter, as appearing in /proc/bus/i2c # $_[2]: The driver of the adapter # @_[3]: Addresses not to scan (array reference) sub scan_adapter { my ($adapter_nr, $adapter_name, $adapter_driver, $not_to_scan) = @_; my ($funcs, $chip, $addr, $conf, @chips, $new_hash, $other_addr); # As we modify it, we need a copy my @not_to_scan = @$not_to_scan; open(local *FILE, "$dev_i2c$adapter_nr") or (print "Can't open $dev_i2c$adapter_nr\n"), return; binmode FILE; # Can we probe this adapter? $funcs = i2c_get_funcs(\*FILE); if ($funcs < 0) { print "Adapter failed to provide its functionalities, skipping.\n"; return; } if (!($funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE))) { print "Adapter cannot be probed, skipping.\n"; return; } if (~$funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE)) { print "Adapter doesn't support all probing functions.\n", "Some addresses won't be probed.\n"; } # Now scan each address in turn foreach $addr (@{$i2c_addresses_to_scan}) { # As the not_to_scan list is sorted, we can check it fast shift @not_to_scan # User skipped an address which we didn't intend to probe anyway while (@not_to_scan and $not_to_scan[0] < $addr); if (@not_to_scan and $not_to_scan[0] == $addr) { shift @not_to_scan; next; } if (!i2c_set_slave_addr(\*FILE, $addr)) { # If the address is busy, in Linux 2.6 we can find out which driver # is using it, and we assume it is the right one. In Linux 2.4 we # just give up and warn the user. my ($device, $driver); if (defined($sysfs_root)) { $device = sprintf("$sysfs_root/bus/i2c/devices/\%d-\%04x", $adapter_nr, $addr); $driver = sysfs_device_driver($device); } if (defined($driver)) { $new_hash = { conf => 6, # Arbitrary confidence i2c_addr => $addr, chipname => sysfs_device_attribute($device, "name") || "unknown", i2c_adap => $adapter_name, i2c_driver => $adapter_driver, i2c_devnr => $adapter_nr, }; printf "Client found at address 0x\%02x\n", $addr; printf "Handled by driver `\%s' (already loaded), chip type `\%s'\n", $driver, $new_hash->{chipname}; # Only add it to the list if this is something we would have # detected, else we end up with random i2c chip drivers listed # (for example media/video drivers.) if (exists $modules_supported{$driver}) { add_i2c_to_chips_detected($driver, $new_hash); } else { print " (note: this is probably NOT a sensor chip!)\n"; } } else { printf("Client at address 0x%02x can not be probed - ". "unload all client drivers first!\n", $addr); } next; } next unless i2c_probe(\*FILE, $addr, $funcs); printf "Client found at address 0x%02x\n",$addr; if (!i2c_safety_check(\*FILE)) { print "Seems to be a 1-register-only device, skipping.\n"; next; } $| = 1; foreach $chip (@chip_ids) { if (exists $chip->{i2c_addrs} and contains $addr, @{$chip->{i2c_addrs}}) { printf("\%-60s", sprintf("Probing for `\%s'... ", $chip->{name})); if (($conf,@chips) = &{$chip->{i2c_detect}} (\*FILE ,$addr)) { if ($chip->{driver} eq "not-a-sensor") { print "Yes\n", " (confidence $conf, not a hardware monitoring chip"; } else { print "Success!\n", " (confidence $conf, driver `$chip->{driver}'"; } if (@chips) { print ", other addresses:"; @chips = sort @chips; foreach $other_addr (@chips) { printf(" 0x%02x",$other_addr); } } printf ")\n"; next if ($chip->{driver} eq "not-a-sensor" || $chip->{driver} eq "use-isa-instead"); $new_hash = { conf => $conf, i2c_addr => $addr, chipname => $chip->{name}, i2c_adap => $adapter_name, i2c_driver => $adapter_driver, i2c_devnr => $adapter_nr, }; if (@chips) { my @chips_copy = @chips; $new_hash->{i2c_sub_addrs} = \@chips_copy; } add_i2c_to_chips_detected $chip->{driver}, $new_hash; } else { print "No\n"; } } } $| = 0; } } sub scan_isa_bus { my ($chip,$addr,$conf); $| = 1; foreach $chip (@chip_ids) { next if not exists $chip->{isa_addrs} or not exists $chip->{isa_detect}; foreach $addr (@{$chip->{isa_addrs}}) { printf("\%-60s", sprintf("Probing for `\%s'\ at 0x\%x... ", $chip->{name}, $addr)); $conf = &{$chip->{isa_detect}} ($addr); print("No\n"), next if not defined $conf; print "Success!\n"; printf " (confidence %d, driver `%s')\n", $conf, $chip->{driver}; my $new_hash = { conf => $conf, isa_addr => $addr, chipname => $chip->{name} }; $new_hash = add_isa_to_chips_detected $chip->{alias_detect},$chip->{driver}, $new_hash; if ($new_hash) { printf " Alias of the chip on I2C bus `%s', address 0x%04x\n", $new_hash->{i2c_adap},$new_hash->{i2c_addr}; } } } $| = 0; } use vars qw(%superio); # The following are taken from the PNP ISA spec (so it's supposed # to be common to all Super I/O chips): # devidreg: The device ID register(s) # logdevreg: The logical device register # actreg: The activation register within the logical device # actmask: The activation bit in the activation register # basereg: The I/O base register within the logical device %superio = ( devidreg => 0x20, logdevreg => 0x07, actreg => 0x30, actmask => 0x01, basereg => 0x60, ); sub exit_superio { my ($addrreg, $datareg) = @_; # Some chips (SMSC, Winbond) want this outb($addrreg, 0xaa); # Return to "Wait For Key" state (PNP-ISA spec) outb($addrreg, 0x02); outb($datareg, 0x02); } # Guess if an unknown Super-I/O chip has sensors sub guess_superio_ld($$$) { my ($addrreg, $datareg, $typical_addr) = @_; my ($oldldn, $ldn, $addr); # Save logical device number outb($addrreg, $superio{logdevreg}); $oldldn = inb($datareg); for ($ldn = 0; $ldn < 16; $ldn++) { # Select logical device outb($addrreg, $superio{logdevreg}); outb($datareg, $ldn); # Read base I/O address outb($addrreg, $superio{basereg}); $addr = inb($datareg) << 8; outb($addrreg, $superio{basereg} + 1); $addr |= inb($datareg); next unless ($addr & 0xfff8) == $typical_addr; printf " (logical device \%X has address 0x\%x, could be sensors)\n", $ldn, $addr; last; } # Be nice, restore original logical device outb($addrreg, $superio{logdevreg}); outb($datareg, $oldldn); } sub probe_superio($$$) { my ($addrreg, $datareg, $chip) = @_; my ($val, $addr); printf "\%-60s", "Found `$chip->{name}'"; # Does it have hardware monitoring capabilities? if (!exists $chip->{driver}) { print "\n (no information available)\n"; return; } if ($chip->{driver} eq "not-a-sensor") { print "\n (no hardware monitoring capabilities)\n"; return; } if ($chip->{driver} eq "via-smbus-only") { print "\n (hardware monitoring capabilities accessible via SMBus only)\n"; return; } # Switch to the sensor logical device outb($addrreg, $superio{logdevreg}); outb($datareg, $chip->{logdev}); # Check the activation register outb($addrreg, $superio{actreg}); $val = inb($datareg); if (!($val & $superio{actmask})) { print "\n (but not activated)\n"; return; } # Get the IO base register outb($addrreg, $superio{basereg}); $addr = inb($datareg); outb($addrreg, $superio{basereg} + 1); $addr = ($addr << 8) | inb($datareg); if ($addr == 0) { print "\n (but no address specified)\n"; return; } print "Success!\n"; printf " (address 0x\%x, driver `%s')\n", $addr, $chip->{driver}; my $new_hash = { conf => 9, isa_addr => $addr, chipname => $chip->{name} }; add_isa_to_chips_detected $chip->{alias_detect},$chip->{driver}, $new_hash; } # Detection routine for non-standard SMSC Super I/O chips # $_[0]: Super I/O LPC config/index port # $_[1]: Super I/O LPC data port # $_[2]: Reference to array of non-standard chips # Return values: 1 if non-standard chip found, 0 otherwise sub smsc_ns_detect_superio { my ($addrreg, $datareg, $ns_chips) = @_; my ($val, $chip); # read alternate device ID register outb($addrreg, 0x0d); $val = inb($datareg); if ($val == 0x00 || $val == 0xff) { return 0; } print "Yes\n"; foreach $chip (@{$ns_chips}) { if ($chip->{devid} == $val) { probe_superio($addrreg, $datareg, $chip); return 1; } } printf("Found unknown non-standard chip with ID 0x%02x\n", $val); return 1; } sub scan_superio { my ($addrreg, $datareg) = @_; my ($val, $found); printf("Probing for Super-I/O at 0x\%x/0x\%x\n", $addrreg, $datareg); $| = 1; # reset state to avoid false positives exit_superio($addrreg, $datareg); FAMILY: foreach my $family (@superio_ids) { printf("\%-60s", "Trying family `$family->{family}'... "); # write the password foreach $val (@{$family->{enter}->{$addrreg}}) { outb($addrreg, $val); } # call the non-standard detection routine first if it exists if (defined($family->{ns_detect}) && &{$family->{ns_detect}}($addrreg, $datareg, $family->{ns_chips})) { exit_superio($addrreg, $datareg); last FAMILY; } # did it work? outb($addrreg, $superio{devidreg}); $val = inb($datareg); outb($addrreg, $superio{devidreg} + 1); $val = ($val << 8) | inb($datareg); if ($val == 0x0000 || $val == 0xffff) { print "No\n"; next FAMILY; } print "Yes\n"; $found = 0; foreach my $chip (@{$family->{chips}}) { if (($chip->{devid} > 0xff && ($val & ($chip->{devid_mask} || 0xffff)) == $chip->{devid}) || ($chip->{devid} <= 0xff && ($val >> 8) == $chip->{devid})) { probe_superio($addrreg, $datareg, $chip); $found++; } } if (!$found) { printf("Found unknown chip with ID 0x%04x\n", $val); # Guess if a logical device could correspond to sensors guess_superio_ld($addrreg, $datareg, $family->{guess}) if defined $family->{guess}; } exit_superio($addrreg, $datareg); last FAMILY; } $| = 0; } sub scan_cpu($) { my $entry = shift; my $confidence; printf("\%-60s", "$entry->{name}... "); if (defined ($confidence = $entry->{detect}())) { print "Success!\n"; printf " (driver `%s')\n", $entry->{driver}; my $new_hash = { conf => $confidence, chipname => $entry->{name}, }; add_isa_to_chips_detected(undef, $entry->{driver}, $new_hash); } else { print "No\n"; } } ################## # CHIP DETECTION # ################## # This routine allows you to select which chips are optionally added to the # chip detection list. The most common use is to allow for different chip # detection/drivers based on different linux kernels # This routine follows the pattern of the SiS adapter special cases sub chip_special_cases { # Based on the kernel, add the appropriate chip structures to the # chip_ids detection list if (kernel_version_at_least(2, 6, 24)) { push @chip_ids, @chip_fschmd_ids; } else { push @chip_ids, @chip_oldfsc_ids; } if (kernel_version_at_least(2, 6, 0)) { push @chip_ids, @chip_kern26_ids; } else { push @chip_ids, @chip_kern24_ids; } } # Each function returns a confidence value. The higher this value, the more # sure we are about this chip. A Winbond W83781D, for example, will be # detected as a LM78 too; but as the Winbond detection has a higher confidence # factor, you should identify it as a Winbond. # Each function returns a list. The first element is the confidence value; # Each element after it is an SMBus address. In this way, we can detect # chips with several SMBus addresses. The SMBus address for which the # function was called is never returned. # If there are devices which get confused if they are only read from, then # this program will surely confuse them. But we guarantee never to write to # any of these devices. # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (7) if detected. # Registers used: 0x58 sub mtp008_detect { my ($file,$addr) = @_; return if (i2c_smbus_read_byte_data($file,0x58)) != 0xac; return (8); } # $_[0]: Chip to detect (0 = LM78, 1 = LM78-J, 2 = LM79) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, (6) if detected. # Registers used: # 0x40: Configuration # 0x48: Full I2C Address # 0x49: Device ID sub lm78_detect { my $reg; my ($chip,$file,$addr) = @_; return unless i2c_smbus_read_byte_data($file,0x48) == $addr; return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00; $reg = i2c_smbus_read_byte_data($file,0x49); return unless ($chip == 0 and ($reg == 0x00 or $reg == 0x20)) or ($chip == 1 and $reg == 0x40) or ($chip == 2 and ($reg & 0xfe) == 0xc0); return (6); } # $_[0]: Chip to detect (0 = LM78, 1 = LM78-J, 2 = LM79) # $_[1]: Address # Returns: undef if not detected, 6 if detected. # Note: Only address 0x290 is scanned at this moment. sub lm78_isa_detect { my ($chip,$addr) = @_ ; my $val = inb ($addr + 1); return if inb ($addr + 2) != $val or inb ($addr + 3) != $val or inb ($addr + 7) != $val; $val = inb($addr + 5); outb($addr + 5, ~$val & 0x7f); if ((inb ($addr+5) & 0x7f) != (~ $val & 0x7f)) { outb($addr+5,$val); return; } my $readproc = sub { isa_read_byte $addr + 5, $addr + 6, @_ }; return unless (&$readproc(0x40) & 0x80) == 0x00; my $reg = &$readproc(0x49); return unless ($chip == 0 and ($reg == 0x00 or $reg == 0x20)) or ($chip == 1 and $reg == 0x40) or ($chip == 2 and ($reg & 0xfe) == 0xc0); # Explicitly prevent misdetection of Winbond chips $reg = &$readproc(0x4f); return if $reg == 0xa3 || $reg == 0x5c; # Explicitly prevent misdetection of ITE chips $reg = &$readproc(0x58); return if $reg == 0x90; return 6; } # $_[0]: Chip to detect (0 = LM78, 1 = LM78-J, 2 = LM79) # $_[1]: ISA address # $_[2]: I2C file handle # $_[3]: I2C address sub lm78_alias_detect { my ($chip,$isa_addr,$file,$i2c_addr) = @_; my $i; my $readproc = sub { isa_read_byte $isa_addr + 5, $isa_addr + 6, @_ }; return 0 unless &$readproc(0x48) == $i2c_addr; for ($i = 0x2b; $i <= 0x3d; $i ++) { return 0 unless &$readproc($i) == i2c_smbus_read_byte_data($file,$i); } return 1; } # $_[0]: Chip to detect (0 = LM75, 1 = DS75) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address (unused) # Returns: undef if not detected, 3 or 6 if detected; # 6 means that the temperatures make sense; # 3 means that the temperatures look strange; # Registers used: # 0x00: Temperature # 0x01: Configuration # 0x02: Hysteresis # 0x03: Overtemperature Shutdown # 0x04-0x07: No registers # The first detection step is based on the fact that the LM75 has only # four registers, and cycles addresses over 8-byte boundaries. We use the # 0x04-0x07 addresses (unused) to improve the reliability. These are not # real registers and will always return the last returned value. This isn't # documented. # Note that register 0x00 may change, so we can't use the modulo trick on it. # The DS75 is a bit different, it doesn't cycle over 8-byte boundaries, and # all register addresses from 0x04 to 0x0f behave like 0x04-0x07 do for # the LM75. # Not all devices enjoy SMBus read word transactions, so we use read byte # transactions even for the 16-bit registers. The low bits aren't very # useful for detection anyway. sub lm75_detect { my $i; my ($chip, $file, $addr) = @_; my $cur = i2c_smbus_read_byte_data($file, 0x00); my $conf = i2c_smbus_read_byte_data($file,0x01); my $hyst = i2c_smbus_read_byte_data($file, 0x02); my $maxreg = $chip == 1 ? 0x0f : 0x07; for $i (0x04 .. $maxreg) { return if i2c_smbus_read_byte_data($file, $i) != $hyst; } my $os = i2c_smbus_read_byte_data($file, 0x03); for $i (0x04 .. $maxreg) { return if i2c_smbus_read_byte_data($file, $i) != $os; } if ($chip == 0) { for ($i = 8; $i <= 248; $i += 40) { return if i2c_smbus_read_byte_data($file, $i + 0x01) != $conf or i2c_smbus_read_byte_data($file, $i + 0x02) != $hyst or i2c_smbus_read_byte_data($file, $i + 0x03) != $os; } } # All registers hold the same value, obviously a misdetection return if $conf == $cur and $cur == $hyst and $cur == $os; # Unused bits return if $chip == 0 and ($conf & 0xe0); return if $chip == 1 and ($conf & 0x80); # Most probable value ranges return 6 if $cur <= 100 and ($hyst >= 10 && $hyst <= 125) and ($os >= 20 && $os <= 127) and $hyst < $os; return 3; } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, 3 or 6 if detected; # 6 means that the temperatures make sense; # 3 means that the temperatures look strange; # Registers used: # 0x00: Temperature # 0x01: Configuration # 0x02: Hysteresis # 0x03: Overtemperature Shutdown # 0x04: Low limit # 0x05: High limit # 0x06-0x07: No registers # The first detection step is based on the fact that the LM77 has only # six registers, and cycles addresses over 8-byte boundaries. We use the # 0x06-0x07 addresses (unused) to improve the reliability. These are not # real registers and will always return the last returned value. This isn't # documented. # Note that register 0x00 may change, so we can't use the modulo trick on it. # Not all devices enjoy SMBus read word transactions, so we use read byte # transactions even for the 16-bit registers at first. We only use read word # transactions in the end when we are already almost certain that we have an # LM77 chip. sub lm77_detect { my $i; my ($file,$addr) = @_; my $cur = i2c_smbus_read_byte_data($file, 0x00); my $conf = i2c_smbus_read_byte_data($file,0x01); my $hyst = i2c_smbus_read_byte_data($file, 0x02); my $os = i2c_smbus_read_byte_data($file, 0x03); my $low = i2c_smbus_read_byte_data($file, 0x04); return if i2c_smbus_read_byte_data($file, 0x06) != $low; return if i2c_smbus_read_byte_data($file, 0x07) != $low; my $high = i2c_smbus_read_byte_data($file, 0x05); return if i2c_smbus_read_byte_data($file, 0x06) != $high; return if i2c_smbus_read_byte_data($file, 0x07) != $high; for ($i = 8; $i <= 248; $i += 40) { return if i2c_smbus_read_byte_data($file, $i + 0x01) != $conf; return if i2c_smbus_read_byte_data($file, $i + 0x02) != $hyst; return if i2c_smbus_read_byte_data($file, $i + 0x03) != $os; return if i2c_smbus_read_byte_data($file, $i + 0x04) != $low; return if i2c_smbus_read_byte_data($file, $i + 0x05) != $high; } # All registers hold the same value, obviously a misdetection return if $conf == $cur and $cur == $hyst and $cur == $os and $cur == $low and $cur == $high; # Unused bits return if ($conf & 0xe0) or (($cur >> 4) != 0 && ($cur >> 4) != 0xf) or (($hyst >> 4) != 0 && ($hyst >> 4) != 0xf) or (($os >> 4) != 0 && ($os >> 4) != 0xf) or (($low >> 4) != 0 && ($low >> 4) != 0xf) or (($high >> 4) != 0 && ($high >> 4) != 0xf); # Make sure the chip supports SMBus read word transactions $cur = i2c_smbus_read_word_data($file, 0x00); return if $cur < 0; $hyst = i2c_smbus_read_word_data($file, 0x02); return if $hyst < 0; $os = i2c_smbus_read_word_data($file, 0x03); return if $os < 0; $low = i2c_smbus_read_word_data($file, 0x04); return if $low < 0; $high = i2c_smbus_read_word_data($file, 0x05); return if $high < 0; $cur /= 16; $hyst /= 16; $os /= 16; $high /= 16; $low /= 16; # Most probable value ranges return 6 if $cur <= 100 and $hyst <= 40 and ($os >= 20 && $os <= 127) and ($high >= 20 && $high <= 127); return 3; } # $_[0]: Chip to detect (0 = LM92, 1 = LM76, 2 = MAX6633/MAX6634/MAX6635) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 2 or 4 if detected; # Registers used: # 0x01: Configuration (National Semiconductor only) # 0x02: Hysteresis # 0x03: Critical Temp # 0x04: Low Limit # 0x05: High Limit # 0x07: Manufacturer ID (LM92 only) # One detection step is based on the fact that the LM92 and clones have a # limited number of registers, which cycle modulo 16 address values. # Note that register 0x00 may change, so we can't use the modulo trick on it. # Not all devices enjoy SMBus read word transactions, so we use read byte # transactions even for the 16-bit registers at first. We only use read # word transactions in the end when we are already almost certain that we # have an LM92 chip or compatible. sub lm92_detect { my ($chip, $file, $addr) = @_; my $conf = i2c_smbus_read_byte_data($file, 0x01); my $hyst = i2c_smbus_read_byte_data($file, 0x02); my $crit = i2c_smbus_read_byte_data($file, 0x03); my $low = i2c_smbus_read_byte_data($file, 0x04); my $high = i2c_smbus_read_byte_data($file, 0x05); return if $conf == 0 and $hyst == 0 and $crit == 0 and $low == 0 and $high == 0; # Unused bits return if ($chip == 0 || $chip == 1) and ($conf & 0xE0); for (my $i = 0; $i <= 240; $i += 16) { return if i2c_smbus_read_byte_data($file, $i + 0x01) != $conf; return if i2c_smbus_read_byte_data($file, $i + 0x02) != $hyst; return if i2c_smbus_read_byte_data($file, $i + 0x03) != $crit; return if i2c_smbus_read_byte_data($file, $i + 0x04) != $low; return if i2c_smbus_read_byte_data($file, $i + 0x05) != $high; } return if $chip == 0 and i2c_smbus_read_word_data($file, 0x07) != 0x0180; # Make sure the chip supports SMBus read word transactions $hyst = i2c_smbus_read_word_data($file, 0x02); return if $hyst < 0; $crit = i2c_smbus_read_word_data($file, 0x03); return if $crit < 0; $low = i2c_smbus_read_word_data($file, 0x04); return if $low < 0; $high = i2c_smbus_read_word_data($file, 0x05); return if $high < 0; foreach my $temp ($hyst, $crit, $low, $high) { return if $chip == 2 and ($temp & 0x7F00); return if $chip != 2 and ($temp & 0x0700); } return 4 if $chip == 0; return 2; } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, 3 if detected # Registers used: # 0xAA: Temperature # 0xA1: High limit # 0xA2: Low limit # 0xA8: Counter # 0xA9: Slope # 0xAC: Configuration # Detection is weak. We check if bit 4 (NVB) is clear, because it is # unlikely to be set (would mean that EEPROM is currently being accessed). # We also check the value of the counter and slope registers, the datasheet # doesn't mention the possible values but the conversion formula together # with experimental evidence suggest possible sanity checks. # Not all devices enjoy SMBus read word transactions, so we do as much as # possible with read byte transactions first, and only use read word # transactions second. sub ds1621_detect { my ($file,$addr) = @_; my $conf = i2c_smbus_read_byte_data($file, 0xAC); return if ($conf & 0x10); my $counter = i2c_smbus_read_byte_data($file, 0xA8); my $slope = i2c_smbus_read_byte_data($file, 0xA9); return if ($slope != 0x10 || $counter > $slope); my $temp = i2c_smbus_read_word_data($file,0xAA); return if $temp < 0 || ($temp & 0x0f00); # On the DS1631, the following two checks are too strict in theory, # but in practice I very much doubt that anyone will set temperature # limits not a multiple of 0.5 degrees C. my $high = i2c_smbus_read_word_data($file,0xA1); return if $high < 0 || ($high & 0x7f00); my $low = i2c_smbus_read_word_data($file,0xA2); return if $low < 0 || ($low & 0x7f00); return if ($temp == 0 && $high == 0 && $low == 0 && $conf == 0); return 3; } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, 1 to 3 if detected. # Registers used: # 0x00: Configuration register # 0x02: Interrupt state register # 0x2a-0x3d: Limits registers # This one is easily misdetected since it doesn't provide identification # registers. So we have to use some tricks: # - 6-bit addressing, so limits readings modulo 0x40 should be unchanged # - positive temperature limits # - limits order correctness # Hopefully this should limit the rate of false positives, without increasing # the rate of false negatives. # Thanks to Lennard Klein for testing on a non-LM80 chip, which was # previously misdetected, and isn't anymore. For reference, it scored # a final confidence of 0, and changing from strict limit comparisons # to loose comparisons did not change the score. sub lm80_detect { my ($i,$reg); my ($file,$addr) = @_; return if (i2c_smbus_read_byte_data($file,0x00) & 0x80) != 0; return if (i2c_smbus_read_byte_data($file,0x02) & 0xc0) != 0; for ($i = 0x2a; $i <= 0x3d; $i++) { $reg = i2c_smbus_read_byte_data($file,$i); return if i2c_smbus_read_byte_data($file,$i+0x40) != $reg; return if i2c_smbus_read_byte_data($file,$i+0x80) != $reg; return if i2c_smbus_read_byte_data($file,$i+0xc0) != $reg; } # Refine a bit by checking wether limits are in the correct order # (min> 1) - 4; # $confidence is now between -4 and 3 return unless $confidence > 0; return $confidence; } # $_[0]: Chip to detect # (0 = LM82/LM83) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 4 to 8 if detected. # Registers used: # 0x02: Status 1 # 0x03: Configuration # 0x04: Company ID of LM84 # 0x35: Status 2 # 0xfe: Manufacturer ID # 0xff: Chip ID / die revision # We can use the LM84 Company ID register because the LM83 and the LM82 are # compatible with the LM84. # The LM83 chip ID is missing from the datasheet and was contributed by # Magnus Forsstrom: 0x03. # At least some revisions of the LM82 seem to be repackaged LM83, so they # have the same chip ID, and temp2/temp4 will be stuck in "OPEN" state. # For this reason, we don't even try to distinguish between both chips. # Thanks to Ben Gardner for reporting. sub lm83_detect { my ($chip, $file) = @_; return if i2c_smbus_read_byte_data($file,0xfe) != 0x01; my $chipid = i2c_smbus_read_byte_data($file,0xff); return if $chipid != 0x01 && $chipid != 0x03; my $confidence = 4; $confidence++ if (i2c_smbus_read_byte_data($file,0x02) & 0xa8) == 0x00; $confidence++ if (i2c_smbus_read_byte_data($file,0x03) & 0x41) == 0x00; $confidence++ if i2c_smbus_read_byte_data($file,0x04) == 0x00; $confidence++ if (i2c_smbus_read_byte_data($file,0x35) & 0x48) == 0x00; return $confidence; } # $_[0]: Chip to detect # (0 = LM90, 1=LM89/LM99, 2=LM86, 3=ADM1032, 4=MAX6657/MAX6658/MAX6659, # 5 = ADT7461, 6 = MAX6648/MAX6692, 7 = MAX6680/MAX6681, # 8 = W83L771W/G), 9 = TI TMP401 # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 4, 6 or 8 if detected. # The Maxim chips MAX6657, MAX6658 and MAX6659 have a low confidence # value (4) because they don't have a die revision register. # Registers used: # 0x03: Configuration # 0x04: Conversion rate # 0xfe: Manufacturer ID # 0xff: Chip ID / die revision sub lm90_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0xfe); my $cid = i2c_smbus_read_byte_data($file, 0xff); my $conf = i2c_smbus_read_byte_data($file, 0x03); my $rate = i2c_smbus_read_byte_data($file, 0x04); if ($chip == 0) { return if ($conf & 0x2a) != 0; return if $rate > 0x09; return if $mid != 0x01; # National Semiconductor return 8 if $cid == 0x21; # LM90 return 6 if ($cid & 0x0f) == 0x20; } if ($chip == 1) { return if ($conf & 0x2a) != 0; return if $rate > 0x09; return if $mid != 0x01; # National Semiconductor return 8 if $addr == 0x4c and $cid == 0x31; # LM89/LM99 return 8 if $addr == 0x4d and $cid == 0x34; # LM89-1/LM99-1 return 6 if ($cid & 0x0f) == 0x30; } if ($chip == 2) { return if ($conf & 0x2a) != 0; return if $rate > 0x09; return if $mid != 0x01; # National Semiconductor return 8 if $cid == 0x11; # LM86 return 6 if ($cid & 0xf0) == 0x10; } if ($chip == 3) { return if ($conf & 0x3f) != 0; return if $rate > 0x0a; return if $mid != 0x41; # Analog Devices return 8 if ($cid & 0xf0) == 0x40; # ADM1032 } if ($chip == 4) { return if ($conf & 0x1f) != ($mid & 0x0f); # No low nibble, # returns previous low nibble return if $rate > 0x09; return if $mid != 0x4d; # Maxim return if $cid != 0x4d; # No register, returns previous value return 4; } if ($chip == 5) { return if ($conf & 0x1b) != 0; return if $rate > 0x0a; return if $mid != 0x41; # Analog Devices return 8 if $cid == 0x61; # ADT7461 } if ($chip == 6) { return if ($conf & 0x3f) != 0; return if $rate > 0x07; return if $mid != 0x4d; # Maxim return if $cid != 0x59; # MAX6648/MAX6692 return 8; } if ($chip == 7) { return if ($conf & 0x03) != 0; return if $rate > 0x07; return if $mid != 0x4d; # Maxim return if $cid != 0x01; # MAX6680/MAX6681 return 6; } if ($chip == 8) { return if ($conf & 0x2a) != 0; return if $rate > 0x09; return if $mid != 0x5c; # Winbond return if $cid != 0x00; # W83L771W/G return 6; } if ($chip == 9) { return if ($conf & 0x1B) != 0; return if $rate > 0x0F; return if $mid != 0x55; # Texas Instruments return if $cid != 0x11; # TMP401 return 6; } return; } # $_[0]: Chip to detect # (1 = LM63, 2 = F75363SG, 3 = LM64) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address (unused) # Returns: undef if not detected, 6 if detected. # Registers used: # 0xfe: Manufacturer ID # 0xff: Chip ID / die revision # 0x03: Configuration (two or three unused bits) # 0x16: Alert mask (two or three unused bits) sub lm63_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0xfe); my $cid = i2c_smbus_read_byte_data($file, 0xff); my $conf = i2c_smbus_read_byte_data($file, 0x03); my $mask = i2c_smbus_read_byte_data($file, 0x16); if ($chip == 1) { return if $mid != 0x01 # National Semiconductor || $cid != 0x41; # LM63 return if ($conf & 0x18) != 0x00 || ($mask & 0xa4) != 0xa4; } elsif ($chip == 2) { return if $mid != 0x23 # Fintek || $cid != 0x20; # F75363SG return if ($conf & 0x1a) != 0x00 || ($mask & 0x84) != 0x00; } elsif ($chip == 3) { return if $mid != 0x01 # National Semiconductor || $cid != 0x51; # LM64 return if ($conf & 0x18) != 0x00 || ($mask & 0xa4) != 0xa4; } return 6; } # $_[0]: Chip to detect # (0 = ADM1029) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address (unused) # Returns: undef if not detected, 6 if detected. # Registers used: # 0x02, 0x03: Fan support # 0x06: Temperature support # 0x07, 0x08, 0x09: Fan config # 0x0d: Manufacturer ID # 0x0e: Chip ID / die revision sub adm1029_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0x0d); my $cid = i2c_smbus_read_byte_data($file, 0x0e); my $cfg; if ($chip == 0) { return unless $mid == 0x41; # Analog Devices return unless ($cid & 0xF0) == 0x00; # ADM1029 # Extra check on unused bits $cfg = i2c_smbus_read_byte_data($file, 0x02); return unless $cfg == 0x03; $cfg = i2c_smbus_read_byte_data($file, 0x06); return unless ($cfg & 0xF9) == 0x01; foreach my $reg (0x03, 0x07, 0x08, 0x09) { $cfg = i2c_smbus_read_byte_data($file, $reg); return unless ($cfg & 0xFC) == 0x00; } return 7; } return; } # $_[0]: Chip to detect # (0 = ADM1030, 1=ADM1031) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 3 to 7 (ADM1031) or 9 (ADM1030) # if detected. # Registers used: # 0x01: Config 2 # 0x03: Status 2 # 0x0d, 0x0e, 0x0f: Temperature offsets # 0x22: Fan speed config # 0x3d: Chip ID # 0x3e: Manufacturer ID # 0x3f: Die revision sub adm1031_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0x3e); my $cid = i2c_smbus_read_byte_data($file, 0x3d); my $drev = i2c_smbus_read_byte_data($file, 0x3f); my $conf2 = i2c_smbus_read_byte_data($file, 0x01); my $stat2 = i2c_smbus_read_byte_data($file, 0x03); my $fsc = i2c_smbus_read_byte_data($file, 0x22); my $lto = i2c_smbus_read_byte_data($file, 0x0d); my $r1to = i2c_smbus_read_byte_data($file, 0x0e); my $r2to = i2c_smbus_read_byte_data($file, 0x0f); my $confidence = 3; if ($chip == 0) { return if $mid != 0x41; # Analog Devices return if $cid != 0x30; # ADM1030 $confidence++ if ($drev & 0x70) == 0x00; $confidence++ if ($conf2 & 0x4A) == 0x00; $confidence++ if ($stat2 & 0x3F) == 0x00; $confidence++ if ($fsc & 0xF0) == 0x00; $confidence++ if ($lto & 0x70) == 0x00; $confidence++ if ($r1to & 0x70) == 0x00; return $confidence; } if ($chip == 1) { return if $mid != 0x41; # Analog Devices return if $cid != 0x31; # ADM1031 $confidence++ if ($drev & 0x70) == 0x00; $confidence++ if ($lto & 0x70) == 0x00; $confidence++ if ($r1to & 0x70) == 0x00; $confidence++ if ($r2to & 0x70) == 0x00; return $confidence; } return; } # $_[0]: Chip to detect # (0 = ADM1033, 1 = ADM1034) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address (unused) # Returns: undef if not detected, 4 or 6 if detected. # Registers used: # 0x3d: Chip ID # 0x3e: Manufacturer ID # 0x3f: Die revision sub adm1034_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0x3e); my $cid = i2c_smbus_read_byte_data($file, 0x3d); my $drev = i2c_smbus_read_byte_data($file, 0x3f); if ($chip == 0) { return if $mid != 0x41; # Analog Devices return if $cid != 0x33; # ADM1033 return if ($drev & 0xf8) != 0x00; return 6 if $drev == 0x02; return 4; } if ($chip == 1) { return if $mid != 0x41; # Analog Devices return if $cid != 0x34; # ADM1034 return if ($drev & 0xf8) != 0x00; return 6 if $drev == 0x02; return 4; } return } # $_[0]: Chip to detect # (0 = ADT7467/ADT7468, 1 = ADT7476, 2 = ADT7462, 3 = ADT7466, # 4 = ADT7470) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 5 or 7 if detected. # Registers used: # 0x3d: Chip ID # 0x3e: Manufacturer ID # 0x3f: Die revision sub adt7467_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0x3e); my $cid = i2c_smbus_read_byte_data($file, 0x3d); my $drev = i2c_smbus_read_byte_data($file, 0x3f); if ($chip == 0) { return if $mid != 0x41; # Analog Devices return if $cid != 0x68; # ADT7467 return if ($drev & 0xf0) != 0x70; return 7 if ($drev == 0x71 || $drev == 0x72); return 5; } if ($chip == 1) { return if $mid != 0x41; # Analog Devices return if $cid != 0x76; # ADT7476 return if ($drev & 0xf0) != 0x60; return 7 if ($drev == 0x69); return 5; } if ($chip == 2) { return if $mid != 0x41; # Analog Devices return if $cid != 0x62; # ADT7462 return if ($drev & 0xf0) != 0x00; return 7 if ($drev == 0x04); return 5; } if ($chip == 3) { return if $mid != 0x41; # Analog Devices return if $cid != 0x66; # ADT7466 return if ($drev & 0xf0) != 0x00; return 7 if ($drev == 0x02); return 5; } if ($chip == 4) { return if $mid != 0x41; # Analog Devices return if $cid != 0x70; # ADT7470 return if ($drev & 0xf0) != 0x00; return 7 if ($drev == 0x00); return 5; } return } # $_[0]: Chip to detect # (0 = ADT7473, 1 = ADT7475) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address (unused) # Returns: undef if not detected, 5 if detected. # Registers used: # 0x3d: Chip ID # 0x3e: Manufacturer ID sub adt7473_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0x3e); my $cid = i2c_smbus_read_byte_data($file, 0x3d); if ($chip == 0) { return if $mid != 0x41; # Analog Devices return if $cid != 0x73; # ADT7473 return 5; } if ($chip == 1) { return if $mid != 0x41; # Analog Devices return if $cid != 0x75; # ADT7475 return 5; } return } # $_[0]: Chip to detect # (0 = aSC7512, 1 = aSC7611, 2 = aSC7621) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address (unused) # Returns: undef if not detected, 1 if detected. # Registers used: # 0x3e: Manufacturer ID (0x61) # 0x3f: Version sub andigilog_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0x3e); my $cid = i2c_smbus_read_byte_data($file, 0x3f); return if ($mid != 0x61); if ($chip == 0) { return if $cid != 0x62; return 5; } if ($chip == 1) { return if $cid != 0x69; return 5; } if ($chip == 2) { return if $cid != 0x6C; return 5; } return; } # $_[0]: Chip to detect # (0 = aSC7511) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address (unused) # Returns: undef if not detected, 1 if detected. # Registers used: # 0xfe: Manufacturer ID # 0xff: Die Code sub andigilog_aSC7511_detect { my ($chip, $file, $addr) = @_; my $mid = i2c_smbus_read_byte_data($file, 0xfe); my $die = i2c_smbus_read_byte_data($file, 0xff); if ($chip == 0) { return if $mid != 0x61; # Andigilog if ($die == 0x0) { return 3; } else { return 1; } } return; } # $_[0]: Vendor to check for # (0x01 = National Semi, 0x41 = Analog Dev, 0x5c = SMSC) # $_[1]: A reference to the file descriptor to access this chip. # #_[2]: Base address. # Returns: undef if not detected, (7) or (8) if detected. # Registers used: 0x3e == Vendor register. # 0x3d == Device ID register (Analog Devices only). # 0x3f == Version/Stepping register. # Constants used: 0x01 == National Semiconductor Vendor Id. # 0x41 == Analog Devices Vendor Id. # 0x5c == SMSC Vendor Id. # 0x60 == Version number. The lower 4 stepping # bits are masked and ignored. sub lm85_detect { my ($vendor,$file,$addr) = @_; return if (i2c_smbus_read_byte_data($file,0x3e)) != $vendor ; my $verstep = i2c_smbus_read_byte_data($file,0x3f); return if ($verstep & 0xf0) != 0x60; if ($vendor == 0x41) # Analog Devices { return if i2c_smbus_read_byte_data($file, 0x3d) != 0x27; return (8); } if ($vendor == 0x5c) # SMSC { # Return undef if this is a SCH5027 return if $verstep >= 0x69 and i2c_smbus_read_byte_data($file, 0xba) == 0x0f; } return (7); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (7) if detected. # Registers used: 0x3E, 0x3F # Assume lower 2 bits of reg 0x3F are for revisions. sub lm87_detect { my ($file,$addr) = @_; return if (i2c_smbus_read_byte_data($file,0x3e)) != 0x02; return if (i2c_smbus_read_byte_data($file,0x3f) & 0xfc) != 0x04; return (7); } # $_[0]: Chip to detect (0 = W83781D, 1 = W83782D, 2 = W83783S, # 3 = W83627HF, 4 = AS99127F (rev.1), # 5 = AS99127F (rev.2), 6 = ASB100, 7 = W83791D, # 8 = W83792D, 9 = W83627EHF 10 = W83627DHG) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, (8,addr1,addr2) if detected, but only # if the LM75 chip emulation is enabled. # Registers used: # 0x48: Full I2C Address # 0x4a: I2C addresses of emulated LM75 chips # 0x4e: Vendor ID byte selection, and bank selection # 0x4f: Vendor ID # 0x58: Device ID (only when in bank 0) # Note: Fails if the W8378xD is not in bank 0! # Note: Detection overrules a previous LM78 detection # Note: Asus chips do not have their I2C address at register 0x48? # AS99127F rev.1 and ASB100 have 0x00, confirmation wanted for # AS99127F rev.2. sub w83781d_detect { my ($reg1,$reg2,@res); my ($chip,$file,$addr) = @_; return unless (i2c_smbus_read_byte_data($file,0x48) == $addr) or ($chip >= 4 && $chip <= 6); $reg1 = i2c_smbus_read_byte_data($file,0x4e); $reg2 = i2c_smbus_read_byte_data($file,0x4f); if ($chip == 4) { # Asus AS99127F (rev.1) return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xc3) or (($reg1 & 0x80) == 0x80 and $reg2 == 0x12); } elsif ($chip == 6) { # Asus ASB100 return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0x94) or (($reg1 & 0x80) == 0x80 and $reg2 == 0x06); } else { # Winbond and Asus AS99127F (rev.2) return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xa3) or (($reg1 & 0x80) == 0x80 and $reg2 == 0x5c); } return unless ($reg1 & 0x07) == 0x00; $reg1 = i2c_smbus_read_byte_data($file,0x58); return if $chip == 0 and ($reg1 != 0x10 && $reg1 != 0x11); return if $chip == 1 and $reg1 != 0x30; return if $chip == 2 and $reg1 != 0x40; return if $chip == 3 and $reg1 != 0x21; return if $chip == 4 and $reg1 != 0x31; return if $chip == 5 and $reg1 != 0x31; return if $chip == 6 and $reg1 != 0x31; return if $chip == 7 and $reg1 != 0x71; return if $chip == 8 and $reg1 != 0x7a; return if $chip == 9 and $reg1 != 0xa1; return if $chip == 10 and $reg1 != 0xa2; $reg1 = i2c_smbus_read_byte_data($file,0x4a); # Default address is 0x2d @res = ($addr != 0x2d) ? (7) : (8); return @res if $chip == 9; # No subclients push @res, ($reg1 & 0x07) + 0x48 unless $reg1 & 0x08; push @res, (($reg1 & 0x70) >> 4) + 0x48 unless ($reg1 & 0x80 or $chip == 2); return @res; } # $_[0]: Chip to detect (0 = W83793) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected # 6 if detected and bank different from 0 # (8,addr1,addr2) if detected, band is 0 and LM75 chip emulation # is enabled # Registers used: # 0x0b: Full I2C Address # 0x0c: I2C addresses of emulated LM75 chips # 0x00: Vendor ID byte selection, and bank selection(Bank 0,1,2) # 0x0d: Vendor ID(Bank 0,1,2) # 0x0e: Device ID(Bank 0,1,2) sub w83793_detect { my ($bank, $reg, @res); my ($chip, $file, $addr) = @_; $bank = i2c_smbus_read_byte_data($file, 0x00); $reg = i2c_smbus_read_byte_data($file, 0x0d); return unless (($bank & 0x80) == 0x00 and $reg == 0xa3) or (($bank & 0x80) == 0x80 and $reg == 0x5c); $reg = i2c_smbus_read_byte_data($file, 0x0e); return if $chip == 0 and $reg != 0x7b; # If bank 0 is selected, we can do more checks return 6 unless ($bank & 0x07) == 0; $reg = i2c_smbus_read_byte_data($file, 0x0b); return unless ($reg == ($addr << 1)); $reg = i2c_smbus_read_byte_data($file, 0x0c); @res = (8); push @res, ($reg & 0x07) + 0x48 unless $reg & 0x08; push @res, (($reg & 0x70) >> 4) + 0x48 unless $reg & 0x80; return @res; } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, 3 if detected # Registers used: # 0x48: Full I2C Address # 0x4e: Vendor ID byte selection # 0x4f: Vendor ID # 0x58: Device ID # Note that the datasheet was useless and this detection routine # is based on dumps we received from users. Also, the W83781SD is *NOT* # a hardware monitoring chip as far as we know, but we still want to # detect it so that people won't keep reporting it as an unknown chip # we should investigate about. sub w83791sd_detect { my ($file, $addr) = @_; my ($reg1, $reg2); return unless (i2c_smbus_read_byte_data($file, 0x48) == $addr); $reg1 = i2c_smbus_read_byte_data($file, 0x4e); $reg2 = i2c_smbus_read_byte_data($file, 0x4f); return unless (!($reg1 & 0x80) && $reg2 == 0xa3) || (($reg1 & 0x80) && $reg2 == 0x5c); $reg1 = i2c_smbus_read_byte_data($file, 0x58); return unless $reg1 == 0x72; return 3; } # $_[0]: Chip to detect (0 = ASM58, 1 = AS2K129R, 2 = ???) # $_[1]: A reference to the file descriptor to access this chip # $_[2]: Address (unused) # Returns: undef if not detected, 5 if detected # Registers used: # 0x4e: Vendor ID high byte # 0x4f: Vendor ID low byte # 0x58: Device ID # Note: The values were given by Alex van Kaam, we don't have datasheets # to confirm. sub mozart_detect { my ($vid,$dev); my ($chip,$file,$addr) = @_; $vid = (i2c_smbus_read_byte_data($file,0x4e) << 8) + i2c_smbus_read_byte_data($file,0x4f); $dev = i2c_smbus_read_byte_data($file,0x58); return if ($chip == 0) and ($dev != 0x56 || $vid != 0x9436); return if ($chip == 1) and ($dev != 0x56 || $vid != 0x9406); return if ($chip == 2) and ($dev != 0x10 || $vid != 0x5ca3); return 5; } # $_[0]: Chip to detect (0 = W83781D, 1 = W83782D, 3 = W83627HF, # 9 = W83627EHF 10 = W83627DHG) # $_[1]: ISA address # $_[2]: I2C file handle # $_[3]: I2C address sub w83781d_alias_detect { my ($chip,$isa_addr,$file,$i2c_addr) = @_; my $i; my $readproc = sub { isa_read_byte $isa_addr + 5, $isa_addr + 6, @_ }; return 0 unless &$readproc(0x48) == $i2c_addr; for ($i = 0x2b; $i <= 0x3d; $i ++) { return 0 unless &$readproc($i) == i2c_smbus_read_byte_data($file,$i); } return 1; } # $_[0]: Chip to detect (0 = W83781D, 1 = W83782D) # $_[1]: Address # Returns: undef if not detected, (8) if detected. sub w83781d_isa_detect { my ($chip,$addr) = @_ ; my ($reg1,$reg2); my $val = inb ($addr + 1); return if inb ($addr + 2) != $val or inb ($addr + 3) != $val or inb ($addr + 7) != $val; $val = inb($addr + 5); outb($addr+5, ~$val & 0x7f); if ((inb ($addr+5) & 0x7f) != (~ $val & 0x7f)) { outb($addr+5,$val); return; } my $read_proc = sub { isa_read_byte $addr + 5, $addr + 6, @_ }; $reg1 = &$read_proc(0x4e); $reg2 = &$read_proc(0x4f); return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xa3) or (($reg1 & 0x80) == 0x80 and $reg2 == 0x5c); return unless ($reg1 & 0x07) == 0x00; $reg1 = &$read_proc(0x58); return if $chip == 0 and ($reg1 & 0xfe) != 0x10; return if $chip == 1 and ($reg1 & 0xfe) != 0x30; return 8; } # $_[0]: Chip to detect (0 = Revision 0x00, 1 = Revision 0x80) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, (6) if detected. # Registers used: # 0x00: Device ID # 0x01: Revision ID # 0x03: Configuration # Mediocre detection sub gl518sm_detect { my $reg; my ($chip,$file,$addr) = @_; return unless i2c_smbus_read_byte_data($file,0x00) == 0x80; return unless (i2c_smbus_read_byte_data($file,0x03) & 0x80) == 0x00; $reg = i2c_smbus_read_byte_data($file,0x01); return unless ($chip == 0 and $reg == 0x00) or ($chip == 1 and $reg == 0x80); return (6); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (5) if detected. # Registers used: # 0x00: Device ID # 0x01: Revision ID # 0x03: Configuration # Mediocre detection sub gl520sm_detect { my ($file,$addr) = @_; return unless i2c_smbus_read_byte_data($file,0x00) == 0x20; return unless (i2c_smbus_read_byte_data($file,0x03) & 0x80) == 0x00; # The line below must be better checked before I dare to use it. # return unless i2c_smbus_read_byte_data($file,0x01) == 0x00; return (5); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (5) if detected. # Registers used: # 0x00: Device ID # Mediocre detection sub gl525sm_detect { my ($file,$addr) = @_; return unless i2c_smbus_read_byte_data($file,0x00) == 0x25; return (5); } # $_[0]: Chip to detect (0 = ADM9240, 1 = DS1780, 2 = LM81) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, (7) if detected. # Registers used: # 0x3e: Company ID # 0x40: Configuration # 0x48: Full I2C Address # Note: Detection overrules a previous LM78 detection sub adm9240_detect { my $reg; my ($chip, $file,$addr) = @_; $reg = i2c_smbus_read_byte_data($file,0x3e); return unless ($chip == 0 and $reg == 0x23) or ($chip == 1 and $reg == 0xda) or ($chip == 2 and $reg == 0x01); return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00; return unless i2c_smbus_read_byte_data($file,0x48) == $addr; return (7); } # $_[0]: Chip to detect (0 = ADM1022, 1 = THMC50, 2 = ADM1028) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x3e: Company ID # 0x3f: Revision # 0x40: Configuration # Note: Detection overrules a previous LM78 or ADM9240 detection sub adm1022_detect { my $reg; my ($chip, $file,$addr) = @_; $reg = i2c_smbus_read_byte_data($file,0x3e); return unless ($chip == 0 and $reg == 0x41) or ($chip == 1 and $reg == 0x49) or ($chip == 2 and $reg == 0x41); $reg = i2c_smbus_read_byte_data($file,0x40); return if ($reg & 0x10); # Soft Reset always reads 0 return if ($chip != 0 and ($reg & 0x80)); # Reserved on THMC50 and ADM1028 $reg = i2c_smbus_read_byte_data($file, 0x3f) & 0xf0; return unless ($chip == 0 and $reg == 0xc0) or ($chip == 1 and $reg == 0xc0) or ($chip == 2 and $reg == 0xd0); return (8); } # $_[0]: Chip to detect (0 = ADM1025, 1 = NE1619) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x3e: Company ID # 0x3f: Revision # 0x40: Configuration # 0x41: Status 1 # 0x42: Status 2 # Note: Detection overrules a previous LM78 or ADM9240 detection sub adm1025_detect { my $reg; my ($chip, $file,$addr) = @_; $reg = i2c_smbus_read_byte_data($file,0x3e); return if ($chip == 0) and ($reg != 0x41); return if ($chip == 1) and ($reg != 0xA1); return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00; return unless (i2c_smbus_read_byte_data($file,0x41) & 0xC0) == 0x00; return unless (i2c_smbus_read_byte_data($file,0x42) & 0xBC) == 0x00; return unless (i2c_smbus_read_byte_data($file,0x3f) & 0xf0) == 0x20; return (8); } # $_[0]: Chip to detect (0 = ADM1026) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x16: Company ID # 0x17: Revision sub adm1026_detect { my $reg; my ($chip, $file,$addr) = @_; $reg = i2c_smbus_read_byte_data($file,0x16); return unless ($reg == 0x41); return unless (i2c_smbus_read_byte_data($file,0x17) & 0xf0) == 0x40; return (8); } # $_[0]: Chip to detect (0 = ADM1024) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x3e: Company ID # 0x3f: Revision # 0x40: Configuration sub adm1024_detect { my $reg; my ($chip, $file,$addr) = @_; $reg = i2c_smbus_read_byte_data($file,0x3e); return unless ($reg == 0x41); return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00; return unless (i2c_smbus_read_byte_data($file,0x3f) & 0xf0) == 0x10; return (8); } # $_[0]: Chip to detect # (0 = ADM1021, 1 = ADM1021A/ADM1023, 2 = MAX1617, 3 = MAX1617A, 4 = THMC10, # 5 = LM84, 6 = GL523, 7 = MC1066) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 3 if simply detected, 5 if detected and # manufacturer ID matches, 7 if detected and manufacturer ID and # revision match # Registers used: # 0x04: Company ID (LM84 only) # 0xfe: Company ID (all but LM84 and MAX1617) # 0xff: Revision (ADM1021, ADM1021A/ADM1023 and MAX1617A) # 0x02: Status # 0x03: Configuration # 0x04: Conversion rate # 0x00-0x01, 0x05-0x08: Temperatures (MAX1617 and LM84) # Note: Especially the MAX1617 has very bad detection; we give it a low # confidence value. sub adm1021_detect { my ($chip, $file, $addr) = @_; my $man_id = i2c_smbus_read_byte_data($file, 0xfe); my $rev = i2c_smbus_read_byte_data($file, 0xff); my $conf = i2c_smbus_read_byte_data($file, 0x03); my $status = i2c_smbus_read_byte_data($file, 0x02); my $convrate = i2c_smbus_read_byte_data($file, 0x04); # Check manufacturer IDs and product revisions when available return if $chip == 0 and $man_id != 0x41 || ($rev & 0xf0) != 0x00; return if $chip == 1 and $man_id != 0x41 || ($rev & 0xf0) != 0x30; return if $chip == 3 and $man_id != 0x4d || $rev != 0x01; return if $chip == 4 and $man_id != 0x49; return if $chip == 5 and $convrate != 0x00; return if $chip == 6 and $man_id != 0x23; return if $chip == 7 and $man_id != 0x54; # Check unused bits if ($chip == 5) # LM84 { return if ($status & 0xab) != 0; return if ($conf & 0x7f) != 0; } else { return if ($status & 0x03) != 0; return if ($conf & 0x3f) != 0; return if ($convrate & 0xf8) != 0; } # Extra checks for MAX1617 and LM84, since those are often misdetected # We verify several assertions (6 for the MAX1617, 4 for the LM84) and # discard the chip if any fail. Note that these checks are not done # by the adm1021 driver. if ($chip == 2 || $chip == 5) { my $lte = i2c_smbus_read_byte_data($file, 0x00); my $rte = i2c_smbus_read_byte_data($file, 0x01); my $lhi = i2c_smbus_read_byte_data($file, 0x05); my $rhi = i2c_smbus_read_byte_data($file, 0x07); my $llo = i2c_smbus_read_byte_data($file, 0x06); my $rlo = i2c_smbus_read_byte_data($file, 0x08); # If all registers hold the same value, it has to be a misdetection return if $lte == $rte and $lte == $lhi and $lte == $rhi and $lte == $llo and $lte == $rlo; # Negative temperatures return if ($lte & 0x80) or ($rte & 0x80); # Negative high limits return if ($lhi & 0x80) or ($rhi & 0x80); # Low limits over high limits if ($chip != 5) # LM84 doesn't have low limits { $llo-=256 if ($llo & 0x80); $rlo-=256 if ($rlo & 0x80); return if ($llo > $lhi) or ($rlo > $rhi); } } return 3 if ($chip == 2) or ($chip == 5); return 7 if $chip <= 3; return 5; } # $_[0]: Chip to detect # (0 = MAX1668, 1 = MAX1805, 2 = MAX1989) # $_[1]: A reference to the file descriptor to access this chip. # We may assume an i2c_set_slave_addr was already done. # $_[2]: Address # Returns: undef if not detected, 7 if detected # Registers used: # 0xfe: Company ID # 0xff: Device ID sub max1668_detect { my ($chip, $file, $addr) = @_; my $man_id = i2c_smbus_read_byte_data($file, 0xfe); my $dev_id = i2c_smbus_read_byte_data($file, 0xff); return if $man_id != 0x4d; return if $chip == 0 and $dev_id != 0x03; return if $chip == 1 and $dev_id != 0x05; return if $chip == 2 and $dev_id != 0x0b; return 7; } # $_[0]: Chip to detect # (0 = MAX1619) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 7 if detected # Registers used: # 0xfe: Company ID # 0xff: Device ID # 0x02: Status # 0x03: Configuration # 0x04: Conversion rate sub max1619_detect { my ($chip, $file, $addr) = @_; my $man_id = i2c_smbus_read_byte_data($file, 0xfe); my $dev_id = i2c_smbus_read_byte_data($file, 0xff); my $conf = i2c_smbus_read_byte_data($file, 0x03); my $status = i2c_smbus_read_byte_data($file, 0x02); my $convrate = i2c_smbus_read_byte_data($file, 0x04); return if $man_id != 0x4D or $dev_id != 0x04 or ($conf & 0x03) or ($status & 0x61) or $convrate >= 8; return 7; } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address (unused) # Returns: undef if not detected, 6 if detected. # Registers used: # 0x28: User ID # 0x29: User ID2 # 0x2A: Version ID sub ite_overclock_detect { my ($file, $addr) = @_; my $uid1 = i2c_smbus_read_byte_data($file, 0x28); my $uid2 = i2c_smbus_read_byte_data($file, 0x29); return if $uid1 != 0x83 || $uid2 != 0x12; return 6; } # $_[0]: Chip to detect (0 = IT8712F) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 7 or 8 if detected (tops LM78). # Registers used: # 0x00: Configuration # 0x48: Full I2C Address # 0x58: Mfr ID # 0x5b: Device ID (not on IT8705) sub ite_detect { my $reg; my ($chip,$file,$addr) = @_; return unless i2c_smbus_read_byte_data($file,0x48) == $addr; return unless (i2c_smbus_read_byte_data($file, 0x00) & 0x90) == 0x10; return unless i2c_smbus_read_byte_data($file,0x58) == 0x90; return if $chip == 0 and i2c_smbus_read_byte_data($file,0x5b) != 0x12; return (7 + ($addr == 0x2d)); } # $_[0]: Chip to detect (0 = IT8712F) # $_[1]: ISA address # $_[2]: I2C file handle # $_[3]: I2C address sub ite_alias_detect { my ($chip,$isa_addr,$file,$i2c_addr) = @_; my $i; my $readproc = sub { isa_read_byte $isa_addr + 5, $isa_addr + 6, @_ }; return 0 unless &$readproc(0x48) == $i2c_addr; for ($i = 0x30; $i <= 0x45; $i++) { return 0 unless &$readproc($i) == i2c_smbus_read_byte_data($file,$i); } return 1; } # $_[0]: A reference to the file descriptor to access this chip # $_[1]: Address # Returns: 8 for a memory eeprom # Registers used: # 0-63: SPD Data and Checksum sub eeprom_detect { my ($file, $addr) = @_; my $checksum = 0; # Check the checksum for validity (works for most DIMMs and RIMMs) for (my $i = 0; $i <= 62; $i++) { $checksum += i2c_smbus_read_byte_data($file, $i); } $checksum &= 255; return 8 if $checksum == i2c_smbus_read_byte_data($file, 63); return; } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, 8 if detected. # Registers used: # 0x00..0x07: DDC signature sub ddcmonitor_detect { my ($file,$addr) = @_; return unless i2c_smbus_read_byte_data($file,0x00) == 0x00 and i2c_smbus_read_byte_data($file,0x01) == 0xFF and i2c_smbus_read_byte_data($file,0x02) == 0xFF and i2c_smbus_read_byte_data($file,0x03) == 0xFF and i2c_smbus_read_byte_data($file,0x04) == 0xFF and i2c_smbus_read_byte_data($file,0x05) == 0xFF and i2c_smbus_read_byte_data($file,0x06) == 0xFF and i2c_smbus_read_byte_data($file,0x07) == 0x00; return 8; } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x00-0x02: Identification ('P','E','G' -> Pegasus ? :-) aka Poseidon I sub fscpeg_detect { my ($file,$addr) = @_; # check the first 3 registers if (i2c_smbus_read_byte_data($file,0x00) != 0x50) { return; } if (i2c_smbus_read_byte_data($file,0x01) != 0x45) { return; } if (i2c_smbus_read_byte_data($file,0x02) != 0x47) { return; } return (8); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x00-0x02: Identification 'P','O','S' -> Poseideon II sub fscpos_detect { my ($file,$addr) = @_; # check the first 3 registers if (i2c_smbus_read_byte_data($file,0x00) != 0x50) { return; } if (i2c_smbus_read_byte_data($file,0x01) != 0x4F) { return; } if (i2c_smbus_read_byte_data($file,0x02) != 0x53) { return; } return (8); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x00-0x02: Identification ('S','C','Y') sub fscscy_detect { my ($file,$addr) = @_; # check the first 3 registers if (i2c_smbus_read_byte_data($file,0x00) != 0x53) { return; } if (i2c_smbus_read_byte_data($file,0x01) != 0x43) { return; } if (i2c_smbus_read_byte_data($file,0x02) != 0x59) { return; } return (8); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x00-0x02: Identification ('H','E','R') sub fscher_detect { my ($file,$addr) = @_; # check the first 3 registers if (i2c_smbus_read_byte_data($file,0x00) != 0x48) { return; } if (i2c_smbus_read_byte_data($file,0x01) != 0x45) { return; } if (i2c_smbus_read_byte_data($file,0x02) != 0x52) { return; } return (8); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x00-0x02: Identification ('H','M','D') sub fschmd_detect { my ($file,$addr) = @_; # check the first 3 registers if (i2c_smbus_read_byte_data($file,0x00) != 0x48) { return; } if (i2c_smbus_read_byte_data($file,0x01) != 0x4D) { return; } if (i2c_smbus_read_byte_data($file,0x02) != 0x44) { return; } return (8); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (8) if detected. # Registers used: # 0x00-0x02: Identification ('H','R','C') sub fschrc_detect { my ($file,$addr) = @_; # check the first 3 registers if (i2c_smbus_read_byte_data($file,0x00) != 0x48) { return; } if (i2c_smbus_read_byte_data($file,0x01) != 0x52) { return; } if (i2c_smbus_read_byte_data($file,0x02) != 0x43) { return; } return (8); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address (unused) # Returns: undef if not detected, 5 if detected. # Registers used: # 0x3E: Manufacturer ID # 0x3F: Version/Stepping sub lm93_detect { my $file = shift; return unless i2c_smbus_read_byte_data($file, 0x3E) == 0x01 and i2c_smbus_read_byte_data($file, 0x3F) == 0x73; return 5; } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, (7) if detected. # Registers used: # 0x3F: Revision ID # 0x48: Address # 0x4A, 0x4B, 0x4F, 0x57, 0x58: Reserved bits. # We do not use 0x49's reserved bits on purpose. The register is named # "VID4/Device ID" so it is doubtful bits 7-1 are really unused. sub m5879_detect { my ($file, $addr) = @_; return unless i2c_smbus_read_byte_data($file, 0x3F) == 0x01; return unless i2c_smbus_read_byte_data($file, 0x48) == $addr; return unless (i2c_smbus_read_byte_data($file, 0x4A) & 0x06) == 0 and (i2c_smbus_read_byte_data($file, 0x4B) & 0xFC) == 0 and (i2c_smbus_read_byte_data($file, 0x4F) & 0xFC) == 0 and (i2c_smbus_read_byte_data($file, 0x57) & 0xFE) == 0 and (i2c_smbus_read_byte_data($file, 0x58) & 0xEF) == 0; return (7); } # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: undef if not detected, 5 or 6 if detected. # Registers used: # 0x3E: Manufacturer ID # 0x3F: Version/Stepping # 0x47: VID (3 reserved bits) # 0x49: VID4 (7 reserved bits) sub smsc47m192_detect { my ($file, $addr) = @_; return unless i2c_smbus_read_byte_data($file, 0x3E) == 0x55 and (i2c_smbus_read_byte_data($file, 0x3F) & 0xF0) == 0x20 and (i2c_smbus_read_byte_data($file, 0x47) & 0x70) == 0x00 and (i2c_smbus_read_byte_data($file, 0x49) & 0xFE) == 0x80; return ($addr == 0x2d ? 6 : 5); } # $_[0]: Chip to detect # (1 = DME1737, 2 = SCH5027) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 5 or 6 if detected. # Registers used: # 0x3E: Manufacturer ID # 0x3F: Version/Stepping # 0x73: Read-only test register (4 test bits) # 0x8A: Read-only test register (7 test bits) # 0xBA: Read-only test register (8 test bits) sub dme1737_detect { my ($chip, $file, $addr) = @_; my $vendor = i2c_smbus_read_byte_data($file, 0x3E); my $verstep = i2c_smbus_read_byte_data($file, 0x3F); return unless $vendor == 0x5C; # SMSC if ($chip == 1) { # DME1737 return unless ($verstep & 0xF8) == 0x88 and (i2c_smbus_read_byte_data($file, 0x73) & 0x0F) == 0x09 and (i2c_smbus_read_byte_data($file, 0x8A) & 0x7F) == 0x4D; } elsif ($chip == 2) { # SCH5027 return unless $verstep >= 0x69 and $verstep <= 0x6F and i2c_smbus_read_byte_data($file, 0xBA) == 0x0F; } return ($addr == 0x2e ? 6 : 5); } # $_[0]: Chip to detect # (1 = F75111R/RG/N, 2 = F75121R/F75122R/RG, 3 = F75373S/SG, # 4 = F75375S/SP, 5 = F75387SG/RG, 6 = F75383M/S/F75384M/S, # 7 = custom power control IC) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address (unused) # Returns: undef if not detected, 7 if detected. # Registers used: # 0x5A-0x5B: Chip ID # 0x5D-0x5E: Vendor ID sub fintek_detect { my ($chip, $file, $addr) = @_; my $chipid = (i2c_smbus_read_byte_data($file, 0x5A) << 8) | i2c_smbus_read_byte_data($file, 0x5B); my $vendid = (i2c_smbus_read_byte_data($file, 0x5D) << 8) | i2c_smbus_read_byte_data($file, 0x5E); return unless $vendid == 0x1934; # Fintek ID if ($chip == 1) { # F75111R/RG/N return unless $chipid == 0x0300; } elsif ($chip == 2) { # F75121R/F75122R/RG return unless $chipid == 0x0301; } elsif ($chip == 3) { # F75373S/SG return unless $chipid == 0x0204; } elsif ($chip == 4) { # F75375S/SP return unless $chipid == 0x0306; } elsif ($chip == 5) { # F75387SG/RG return unless $chipid == 0x0410; } elsif ($chip == 6) { # F75383M/S/F75384M/S # The datasheet has 0x0303, but Fintek say 0x0413 is also possible return unless $chipid == 0x0303 || $chipid == 0x0413; } elsif ($chip == 7) { # custom power control IC return unless $chipid == 0x0302; } return 7; } # This checks for non-FFFF values for temperature, voltage, and current. # The address (0x0b) is specified by the SMBus standard so it's likely # that this really is a smart battery. # $_[0]: A reference to the file descriptor to access this chip. # $_[1]: Address # Returns: 5 sub smartbatt_detect { my ($file,$addr) = @_; # check some registers if (i2c_smbus_read_word_data($file,0x08) == 0xffff) { return; } if (i2c_smbus_read_word_data($file,0x09) == 0xffff) { return; } if (i2c_smbus_read_word_data($file,0x0a) == 0xffff) { return; } return (5); } # Returns: 4 # These are simple detectors that only look for a register at the # standard location. No writes are performed. # For KCS, use the STATUS register. For SMIC, use the FLAGS register. sub ipmi_kcs_detect { return if inb (0x0ca3) == 0xff; return (4); } sub ipmi_smic_detect { return if inb (0x0cab) == 0xff; return (4); } # $_[0]: Chip to detect (0 = W83L784R/AR/G, 1 = W83L785R/G, # 2 = W83L786NR/NG/R/G) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 6 or 8 if detected # Registers used: # 0x40: Configuration # 0x4a: Full I2C Address (W83L784R only) # 0x4b: I2C addresses of emulated LM75 chips (W83L784R only) # 0x4c: Winbond Vendor ID (Low Byte) # 0x4d: Winbond Vendor ID (High Byte) # 0x4e: Chip ID sub w83l784r_detect { my ($reg,@res); my ($chip,$file,$addr) = @_; return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00; return if $chip == 0 and i2c_smbus_read_byte_data($file,0x4a) != $addr; return unless i2c_smbus_read_byte_data($file,0x4c) == 0xa3; return unless i2c_smbus_read_byte_data($file,0x4d) == 0x5c; $reg = i2c_smbus_read_byte_data($file, 0x4e); return if $chip == 0 and $reg != 0x50; return if $chip == 1 and $reg != 0x60; return if $chip == 2 and $reg != 0x80; return 6 if $chip != 0; # No subclients @res = (8); $reg = i2c_smbus_read_byte_data($file, 0x4b); push @res, ($reg & 0x07) + 0x48 unless $reg & 0x08 ; push @res, (($reg & 0x70) >> 4) + 0x48 unless $reg & 0x80; return @res; } # $_[0]: Chip to detect (0 = W83L785TS-S) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 8 if detected # Registers used: # 0x4C-4E: Mfr and Chip ID sub w83l785ts_detect { my ($chip,$file,$addr) = @_; return unless i2c_smbus_read_byte_data($file,0x4c) == 0xa3; return unless i2c_smbus_read_byte_data($file,0x4d) == 0x5c; return unless i2c_smbus_read_byte_data($file,0x4e) == 0x70; return (8); } # $_[0]: Chip to detect. Always zero for now, but available for future use # if somebody finds a way to distinguish MAX6650 and MAX6651. # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # Returns: undef if not detected, 3 if detected. # # The max6650 has no device ID register. However, a few registers have # spare bits, which are documented as being always zero on read. We read # all of these registers check the spare bits. Any non-zero means this # is not a max6650/1. # # The always zero bits are: # configuration byte register (0x02) - top 2 bits # gpio status register (0x14) - top 3 bits # alarm enable register (0x08) - top 3 bits # alarm status register (0x0A) - top 3 bits # tachometer count time register (0x16) - top 6 bits # Additionally, not all values are possible for lower 3 bits of # the configuration register. sub max6650_detect { my ($chip, $file) = @_; my $conf = i2c_smbus_read_byte_data($file,0x02); return if i2c_smbus_read_byte_data($file,0x16) & 0xFC; return if i2c_smbus_read_byte_data($file,0x0A) & 0xE0; return if i2c_smbus_read_byte_data($file,0x08) & 0xE0; return if i2c_smbus_read_byte_data($file,0x14) & 0xE0; return if ($conf & 0xC0) or ($conf & 0x07) > 4; return 3; } # $_[0]: Chip to detect. Always zero. # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address. # # Returns: undef if not detected, 6 if detected. sub max6655_detect { my ($chip, $file, $addr) = @_; # checking RDID (Device ID) return unless i2c_smbus_read_byte_data($file, 0xfe) == 0x0a; # checking RDRV (Manufacturer ID) return unless i2c_smbus_read_byte_data($file, 0xff) == 0x4d; # checking unused bits (conversion rate, extended temperature) return unless i2c_smbus_read_byte_data($file, 0x04) & 0xf8; return unless i2c_smbus_read_byte_data($file, 0x10) & 0x1f; return unless i2c_smbus_read_byte_data($file, 0x11) & 0x1f; return unless i2c_smbus_read_byte_data($file, 0x12) & 0x1f; return 6; } # $_[0]: Chip to detect (0 = VT1211) # $_[1]: A reference to the file descriptor to access this chip. # $_[2]: Address # # This isn't very good detection. # Verify the i2c address, and the stepping ID (which is 0xb0 on # my chip but could be different for others... # sub vt1211_i2c_detect { my ($chip,$file,$addr) = @_; return unless (i2c_smbus_read_byte_data($file,0x48) & 0x7f) == $addr; return unless i2c_smbus_read_byte_data($file,0x3f) == 0xb0; return 2; } # $_[0]: Chip to detect (0 = VT1211) # $_[1]: ISA address # $_[2]: I2C file handle # $_[3]: I2C address sub vt1211_alias_detect { my ($chip,$isa_addr,$file,$i2c_addr) = @_; my $i; return 0 unless (inb($isa_addr + 0x48) & 0x7f) == $i2c_addr; return 0 unless (i2c_smbus_read_byte_data($file,0x48) & 0x7f) == $i2c_addr; for ($i = 0x2b; $i <= 0x3d; $i ++) { return 0 unless inb($isa_addr + $i) == i2c_smbus_read_byte_data($file,$i); } return 1; } ###################### # PCI CHIP DETECTION # ###################### # Returns: undef if not detected, (7) or (9) if detected. # The address is encoded in PCI space. We could decode it and print it. # For Linux 2.4 we should probably check for invalid matches (SiS645). sub sis5595_pci_detect { return unless exists $pci_list{'1039:0008'}; return (kernel_version_at_least(2, 6, 0) ? 9 : 7); } # Returns: undef if not detected, (9) if detected. # The address is encoded in PCI space. We could decode it and print it. sub via686a_pci_detect { return unless exists $pci_list{'1106:3057'}; return 9; } # Returns: undef if not detected, (9) if detected. # The address is encoded in PCI space. We could decode it and print it. sub via8231_pci_detect { return unless exists $pci_list{'1106:8235'}; return 9; } # Returns: undef if not detected, (9) if detected. sub k8temp_pci_detect { return unless exists $pci_list{'1022:1103'}; return 9; } sub k10temp_pci_detect { return unless exists $pci_list{'1022:1203'}; return 9; } # Returns: undef if not detected, (9) if detected. # the device 0xa620 should not be visible on host PCI bus, gateway # must be used sub intel_amb_detect { if ((exists $pci_list{'8086:a620'}) || (exists $pci_list{'8086:25f0'})) { return 9; } return; } # Returns: undef if not detected, (9) if detected. sub coretemp_detect { my $probecpu; foreach $probecpu (@cpu) { if ($probecpu->{'vendor_id'} eq 'GenuineIntel' && $probecpu->{'cpu family'} == 6 && ($probecpu->{'model'} == 14 || $probecpu->{'model'} == 15 || $probecpu->{'model'} == 0x16 || $probecpu->{'model'} == 0x17)) { return 9; } } return; } ################ # MAIN PROGRAM # ################ # $_[0]: reference to a list of chip hashes sub print_chips_report { my ($listref) = @_; my $data; foreach $data (@$listref) { my $is_i2c = exists $data->{i2c_addr}; my $is_isa = exists $data->{isa_addr}; print " * "; if ($is_i2c) { printf "Bus `%s'\n", $data->{i2c_adap}; printf " Busdriver `%s', I2C address 0x%02x", $data->{i2c_driver}, $data->{i2c_addr}; if (exists $data->{i2c_sub_addrs}) { print " (and"; my $sub_addr; foreach $sub_addr (@{$data->{i2c_sub_addrs}}) { printf " 0x%02x",$sub_addr; } print ")" } print "\n "; } if ($is_isa) { print "ISA bus"; if ($data->{isa_addr}) { printf ", address 0x%x", $data->{isa_addr}; } print " (Busdriver `i2c-isa')" unless kernel_version_at_least(2, 6, 18); print "\n "; } printf "Chip `%s' (confidence: %d)\n", $data->{chipname}, $data->{conf}; } } # We build here an array adapters, indexed on the number the adapter has # at this moment (we assume only loaded adapters are interesting at all; # everything that got scanned also got loaded). Each entry is a reference # to a hash containing: # driver: Name of the adapter driver # nr_now: Number of the bus now # nr_later: Number of the bus when the modprobes are done (not included if the # driver should not be loaded) # A second array, called sub generate_modprobes { my ($chip,$detection,$nr,$i,@optionlist,@probelist,$driver,$isa,$adap); my $ipmi = 0; my $modprobes = ""; my $configfile; # Collect all adapters used $nr = 0; $isa = 0; foreach $chip (@chips_detected) { foreach $detection (@{$chip->{detected}}) { # If there is more than one bus detected by a driver, they are # still all added. So we number them in the correct order if (exists $detection->{i2c_driver} and not exists $i2c_adapters[$detection->{i2c_devnr}]->{nr_later} and not exists $detection->{isa_addr}) { # Always use ISA access if possible foreach $adap (@i2c_adapters) { next unless exists $adap->{driver}; $adap->{nr_later} = $nr++ if $adap->{driver} eq $detection->{i2c_driver}; } } if (exists $detection->{isa_addr}) { $isa=1; } if ($chip->{driver} eq "bmcsensors" || $chip->{driver} eq "ipmisensors") { $ipmi = 1; } } } $modprobes .= "# I2C adapter drivers\n" if $nr; for ($i = 0; $i < $nr; $i++) { foreach $adap (@i2c_adapters) { next unless exists $adap->{nr_later} and $adap->{nr_later} == $i; if ($adap->{driver} eq "UNKNOWN") { $modprobes .= "# modprobe unknown adapter ".$adap->{name}."\n"; } else { $modprobes .= "$adap->{driver}\n" unless $modprobes =~ /$adap->{driver}\n/; } last; } } # i2c-isa is loaded automatically (as a dependency) since 2.6.14, # and will soon be gone. $modprobes .= "i2c-isa\n" if ($isa && !kernel_version_at_least(2, 6, 18)); if ($ipmi) { $modprobes .= "# You must also install and load the IPMI modules\n"; if (kernel_version_at_least(2, 6, 0)) { $modprobes .= "ipmi-si\n"; } else { $modprobes .= "i2c-ipmi\n"; } } # Now determine the chip probe lines $modprobes .= "# Chip drivers\n"; foreach $chip (@chips_detected) { next if not @{$chip->{detected}}; if ($chip->{driver} eq "to-be-written") { $modprobes .= "# no driver for $chip->{detected}[0]{chipname} yet\n"; } else { # need the * for 2.4 kernels, won't necessarily be an exact match open(local *INPUTFILE, "modprobe -l $chip->{driver}\\* 2>/dev/null |"); local $_; my $modulefound = 0; while () { if(m@/@) { $modulefound = 1; last; } } close INPUTFILE; #check return value from modprobe in case modprobe -l isn't supported if((($? >> 8) == 0) && ! $modulefound) { $modprobes .= "# Warning: the required module $chip->{driver} is not currently installed\n". "# on your system. For status of 2.6 kernel ports check\n". "# http://www.lm-sensors.org/wiki/Devices. If driver is built\n". "# into the kernel, or unavailable, comment out the following line.\n"; } $modprobes .= "$chip->{driver}\n"; } # Handle misdetects foreach $detection (@{$chip->{misdetected}}) { push @optionlist, $i2c_adapters[$detection->{i2c_devnr}]->{nr_later}, $detection->{i2c_addr} if exists $detection->{i2c_addr} and exists $i2c_adapters[$detection->{i2c_devnr}]->{nr_later}; push @optionlist, -1, $detection->{isa_addr} if exists $detection->{isa_addr}; } # Handle aliases foreach $detection (@{$chip->{detected}}) { if (exists $detection->{i2c_driver} and exists $detection->{isa_addr} and exists $i2c_adapters[$detection->{i2c_devnr}]->{nr_later}) { push @optionlist, $i2c_adapters[$detection->{i2c_devnr}]->{nr_later}, $detection->{i2c_addr}; } } next if not (@probelist or @optionlist); $configfile = "# hwmon module options\n" unless defined $configfile; $configfile .= "options $chip->{driver}"; $configfile .= sprintf " ignore=%d,0x%02x",shift @optionlist, shift @optionlist if @optionlist; $configfile .= sprintf ",%d,0x%02x",shift @optionlist, shift @optionlist while @optionlist; $configfile .= sprintf " probe=%d,0x%02x",shift @probelist, shift @probelist if @probelist; $configfile .= sprintf ",%d,0x%02x",shift @probelist, shift @probelist while @probelist; $configfile .= "\n"; } return ($modprobes,$configfile); } sub main { my (@adapters,$res,$did_adapter_detection,$adapter); # We won't go very far if not root unless ($> == 0) { print "You need to be root to run this script.\n"; exit -1; } if (-x "/sbin/service" && -f "/etc/init.d/lm_sensors" && -f "/var/lock/subsys/lm_sensors") { system("/sbin/service", "lm_sensors", "stop"); } initialize_kernel_version(); initialize_conf; initialize_proc_pci; initialize_modules_list; # make sure any special case chips are added to the chip_ids list before # making the support modules list chip_special_cases(); initialize_modules_supported; initialize_cpu_list(); print "# sensors-detect revision $revision\n\n"; print "This program will help you determine which kernel modules you need\n", "to load to use lm_sensors most effectively. It is generally safe\n", "and recommended to accept the default answers to all questions,\n", "unless you know what you're doing.\n"; print "You need to have i2c and lm_sensors installed before running this\n", "program.\n" unless kernel_version_at_least(2, 6, 0); print "\n"; print "We can start with probing for (PCI) I2C or SMBus adapters.\n"; print "Do you want to probe now? (YES/no): "; @adapters = adapter_pci_detection if ($did_adapter_detection = not =~ /\s*[Nn]/); print "\n"; if (not $did_adapter_detection) { print "As you skipped adapter detection, we will only scan already loaded\n". "adapter modules.\n"; } else { print "We will now try to load each adapter module in turn.\n" if (@adapters); foreach $adapter (@adapters) { if (exists($modules_list{$adapter})) { print "Module `$adapter' already loaded.\n"; } else { print "Load `$adapter' (say NO if built into your kernel)? (YES/no): "; unless ( =~ /^\s*[Nn]/) { if (system ("modprobe", $adapter)) { print "Loading failed... skipping.\n"; } else { print "Module loaded successfully.\n"; } } } } } print "If you have undetectable or unsupported I2C/SMBus adapters, you can have\n". "them scanned by manually loading the modules before running this script.\n\n"; initialize_i2c_adapters_list(); if (!exists($modules_list{"i2c-dev"}) && !(defined $sysfs_root && -e "$sysfs_root/class/i2c-dev")) { print "To continue, we need module `i2c-dev' to be loaded.\n"; print "If it is built-in into your kernel, you can safely skip this.\n" unless kernel_version_at_least(2, 6, 0); print "Do you want to load `i2c-dev' now? (YES/no): "; if ( =~ /^\s*n/i) { print "Well, you will know best.\n"; } elsif (system "modprobe", "i2c-dev") { print "Loading failed, expect problems later on.\n"; } else { print "Module loaded successfully.\n"; } print "\n"; } $i2c_addresses_to_scan = i2c_addresses_to_scan(); print "We are now going to do the I2C/SMBus adapter probings. Some chips may\n", "be double detected; we choose the one with the highest confidence\n", "value in that case.\n", "If you found that the adapter hung after probing a certain address,\n", "you can specify that address to remain unprobed.\n"; my ($inp,@not_to_scan,$inp2); for (my $dev_nr = 0; $dev_nr < @i2c_adapters; $dev_nr++) { next unless exists $i2c_adapters[$dev_nr]; my $adap = $i2c_adapters[$dev_nr]->{'name'}; print "\n"; print "Next adapter: $adap (i2c-$dev_nr)\n"; print "Do you want to scan it? (YES/no/selectively): "; $inp = ; if ($inp =~ /^\s*[Ss]/) { print "Please enter one or more addresses not to scan. Separate them ", "with comma's.\n", "You can specify a range by using dashes. Addresses may be ", "decimal (like 54)\n", "or hexadecimal (like 0x33).\n", "Addresses: "; $inp2 = ; chop $inp2; @not_to_scan = parse_not_to_scan(0x03, 0x77, $inp2); } scan_adapter $dev_nr, $adap, $i2c_adapters[$dev_nr]->{'driver'}, \@not_to_scan unless $inp =~ /^\s*[Nn]/; } print "\n"; # Skip "random" I/O port probing on PPC if ($kernel_arch ne 'ppc' && $kernel_arch ne 'ppc64') { print "Some chips are also accessible through the ISA I/O ports. We have to\n". "write to arbitrary I/O ports to probe them. This is usually safe though.\n". "Yes, you do have ISA I/O ports even if you do not have any ISA slots!\n"; print "Do you want to scan the ISA I/O ports? (YES/no): "; unless ( =~ /^\s*n/i) { initialize_ioports(); scan_isa_bus(); close_ioports(); } print "\n"; print "Some Super I/O chips may also contain sensors. We have to write to\n". "standard I/O ports to probe them. This is usually safe.\n"; print "Do you want to scan for Super I/O sensors? (YES/no): "; unless ( =~ /^\s*n/i) { initialize_ioports(); scan_superio(0x2e, 0x2f); scan_superio(0x4e, 0x4f); close_ioports(); } print "\n"; } print "Some south bridges, CPUs or memory controllers may also contain\n"; print "embedded sensors. Do you want to scan for them? (YES/no): "; unless ( =~ /^\s*n/i) { $| = 1; foreach my $entry (@cpu_ids) { scan_cpu($entry); } $| = 0; } print "\n"; if(! @chips_detected) { print "Sorry, no sensors were detected.\n", "Either your sensors are not supported, or they are connected to an\n", "I2C or SMBus adapter that is not supported. See\n", "http://www.lm-sensors.org/wiki/FAQ/Chapter3 for further information.\n", "If you find out what chips are on your board, check\n", "http://www.lm-sensors.org/wiki/Devices for driver status.\n"; exit; } print "Now follows a summary of the probes I have just done.\n". "Just press ENTER to continue: "; ; my ($chip,$data); foreach $chip (@chips_detected) { print "\nDriver `$chip->{driver}' "; if (@{$chip->{detected}}) { if (@{$chip->{misdetected}}) { print "(should be inserted but causes problems):\n"; } else { print "(should be inserted):\n"; } } else { if (@{$chip->{misdetected}}) { print "(may not be inserted):\n"; } else { print "(should not be inserted, but is harmless):\n"; } } if (@{$chip->{detected}}) { print " Detects correctly:\n"; print_chips_report $chip->{detected}; } if (@{$chip->{misdetected}}) { print " Misdetects:\n"; print_chips_report $chip->{misdetected}; } } print "\n"; print "I will now generate the commands needed to load the required modules.\n". "Just press ENTER to continue: "; ; print "\n"; my ($modprobes, $configfile) = generate_modprobes(); if (defined $configfile) { print "To make the sensors modules behave correctly, add these lines to\n". "/etc/modprobe.d/lm-sensors:\n\n"; print "#----cut here----\n". $configfile. "#----cut here----\n\n"; } print "To load everything that is needed, add this to /etc/modules:\n\n"; print "#----cut here----\n". $modprobes. "#----cut here----\n\n"; if ($modprobes =~ /i2c-viapro/ && $modprobes =~ /via686a/) { print("WARNING: Please note that i2c-viapro and via686a may conflict which\n", "each other. If you get 'sensors not found', try loading only one module\n", "at the same time.\n\n"); } print "Do you want to add these lines automatically? (yes/NO)"; $_ = ; if (m/^\s*[Yy]/) { if (defined $configfile) { open(MODPROBE, ">>/etc/modprobe.d/lm-sensors") or die "Sorry, can't create /etc/modprobe.d/lm-sensors ($!)?!?"; print MODPROBE "# Generated by sensors-detect on " . scalar localtime() . "\n"; print MODPROBE $configfile; close(MODPROBE); } open(MODULES, ">>/etc/modules") or die "Sorry, can't create /etc/modules ($!)?!?"; print MODULES "\n# Generated by sensors-detect on " . scalar localtime() . "\n"; print MODULES $modprobes; close(MODULES); } } main;