mirror of
https://github.com/lm-sensors/lm-sensors
synced 2025-08-22 18:08:28 +00:00
7733 lines
216 KiB
Perl
Executable File
7733 lines
216 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
#
|
|
# sensors-detect - Detect hardware monitoring chips
|
|
# Copyright (C) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>
|
|
# Copyright (C) 2004 - 2015 Jean Delvare <jdelvare@suse.de>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
# MA 02110-1301 USA.
|
|
#
|
|
|
|
require 5.004;
|
|
|
|
use strict;
|
|
use Fcntl qw(:DEFAULT :seek);
|
|
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 constant NO_CACHE => 1;
|
|
use constant LM_VERSION => '3.6.0+git';
|
|
use vars qw(@pci_adapters @chip_ids @ipmi_ifs @non_hwmon_chip_ids
|
|
$i2c_addresses_to_scan @i2c_byte_cache %opt);
|
|
|
|
# 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 (Device name) and driver (Device driver).
|
|
@pci_adapters = (
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x7113,
|
|
procid => "Intel 82371AB PIIX4 ACPI",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x7603,
|
|
procid => "Intel 82372FB PIIX5 ACPI",
|
|
driver => "to-be-written",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x719b,
|
|
procid => "Intel 82443MX Mobile",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x2413,
|
|
procid => "Intel 82801AA ICH",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x2423,
|
|
procid => "Intel 82801AB ICH0",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x2443,
|
|
procid => "Intel 82801BA ICH2",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x2483,
|
|
procid => "Intel 82801CA/CAM ICH3",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x24C3,
|
|
procid => "Intel 82801DB ICH4",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x24D3,
|
|
procid => "Intel 82801EB ICH5",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x25A4,
|
|
procid => "Intel 6300ESB",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x269B,
|
|
procid => "Intel Enterprise Southbridge - ESB2",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x266A,
|
|
procid => "Intel 82801FB ICH6",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x27DA,
|
|
procid => "Intel 82801G ICH7",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x283E,
|
|
procid => "Intel 82801H ICH8",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x2930,
|
|
procid => "Intel ICH9",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x5032,
|
|
procid => "Intel Tolapai",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x3A30,
|
|
procid => "Intel ICH10",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x3A60,
|
|
procid => "Intel ICH10",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x3B30,
|
|
procid => "Intel 3400/5 Series (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x1C22,
|
|
procid => "Intel Cougar Point (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x1D22,
|
|
procid => "Intel Patsburg (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x1D70,
|
|
procid => "Intel Patsburg (PCH) IDF",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x1D71,
|
|
procid => "Intel Patsburg (PCH) IDF",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x1D72,
|
|
procid => "Intel Patsburg (PCH) IDF",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x2330,
|
|
procid => "Intel DH89xxCC (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x1E22,
|
|
procid => "Intel Panther Point (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x8C22,
|
|
procid => "Intel Lynx Point (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x9C22,
|
|
procid => "Intel Lynx Point-LP (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x1F3C,
|
|
procid => "Avoton (SOC)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x8D22,
|
|
procid => "Wellsburg (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x8D7D,
|
|
procid => "Wellsburg (PCH) MS",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x8D7E,
|
|
procid => "Wellsburg (PCH) MS",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x8D7F,
|
|
procid => "Wellsburg (PCH) MS",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x23B0,
|
|
procid => "Coleto Creek (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x8CA2,
|
|
procid => "Wildcat Point (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x9CA2,
|
|
procid => "Wildcat Point-LP (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x0F12,
|
|
procid => "BayTrail (SOC)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0xA123,
|
|
procid => "Sunrise Point-H (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x9D23,
|
|
procid => "Sunrise Point-LP (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x19DF,
|
|
procid => "DNV (SOC)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x5AD4,
|
|
procid => "Broxton (SOC)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0xA1A3,
|
|
procid => "Lewisburg (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0xA223,
|
|
procid => "Lewisburg Supersku (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0xA2A3,
|
|
procid => "Kaby Lake (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x31D4,
|
|
procid => "Gemini Lake (SOC)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0xA323,
|
|
procid => "Cannon Lake-H (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x9DA3,
|
|
procid => "Cannon Lake-LP (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x18DF,
|
|
procid => "Cedar Fork (PCH)",
|
|
driver => "i2c-i801",
|
|
}, {
|
|
vendid => 0x8086,
|
|
devid => 0x8119,
|
|
procid => "Intel SCH",
|
|
driver => "i2c-isch",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3040,
|
|
procid => "VIA Technologies VT82C586B Apollo ACPI",
|
|
driver => "i2c-via",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3050,
|
|
procid => "VIA Technologies VT82C596 Apollo ACPI",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3051,
|
|
procid => "VIA Technologies VT82C596B ACPI",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3057,
|
|
procid => "VIA Technologies VT82C686 Apollo ACPI",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3074,
|
|
procid => "VIA Technologies VT8233 VLink South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3147,
|
|
procid => "VIA Technologies VT8233A South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3177,
|
|
procid => "VIA Technologies VT8233A/8235 South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3227,
|
|
procid => "VIA Technologies VT8237 South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3337,
|
|
procid => "VIA Technologies VT8237A South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x8235,
|
|
procid => "VIA Technologies VT8231 South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x3287,
|
|
procid => "VIA Technologies VT8251 South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x8324,
|
|
procid => "VIA Technologies CX700 South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1106,
|
|
devid => 0x8353,
|
|
procid => "VIA Technologies VX800/VX820 South Bridge",
|
|
driver => "i2c-viapro",
|
|
}, {
|
|
vendid => 0x1039,
|
|
devid => 0x0008,
|
|
procid => "Silicon Integrated Systems SIS5595",
|
|
driver => "i2c-sis5595",
|
|
}, {
|
|
vendid => 0x1039,
|
|
devid => 0x0630,
|
|
procid => "Silicon Integrated Systems SIS630",
|
|
driver => "i2c-sis630",
|
|
}, {
|
|
vendid => 0x1039,
|
|
devid => 0x0730,
|
|
procid => "Silicon Integrated Systems SIS730",
|
|
driver => "i2c-sis630",
|
|
}, {
|
|
vendid => 0x1039,
|
|
devid => 0x0016,
|
|
procid => "Silicon Integrated Systems SMBus Controller",
|
|
driver => "i2c-sis96x",
|
|
}, {
|
|
# 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",
|
|
}, {
|
|
vendid => 0x10b9,
|
|
devid => 0x7101,
|
|
procid => "Acer Labs 1535",
|
|
driver => "i2c-ali1535",
|
|
}, {
|
|
vendid => 0x10b9,
|
|
devid => 0x1563,
|
|
procid => "Acer Labs 1563",
|
|
driver => "i2c-ali1563",
|
|
}, {
|
|
vendid => 0x1022,
|
|
devid => 0x740b,
|
|
procid => "AMD-756 Athlon ACPI",
|
|
driver => "i2c-amd756",
|
|
}, {
|
|
vendid => 0x1022,
|
|
devid => 0x7413,
|
|
procid => "AMD-766 Athlon ACPI",
|
|
driver => "i2c-amd756",
|
|
}, {
|
|
vendid => 0x1022,
|
|
devid => 0x7443,
|
|
procid => "AMD-768 System Management",
|
|
driver => "i2c-amd756",
|
|
}, {
|
|
vendid => 0x1022,
|
|
devid => 0x746b,
|
|
procid => "AMD-8111 ACPI",
|
|
driver => "i2c-amd756",
|
|
}, {
|
|
vendid => 0x1022,
|
|
devid => 0x746a,
|
|
procid => "AMD-8111 SMBus 2.0",
|
|
driver => "i2c-amd8111",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x01b4,
|
|
procid => "nVidia nForce SMBus",
|
|
driver => "i2c-amd756",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0064,
|
|
procid => "nVidia Corporation nForce2 SMBus (MCP)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0084,
|
|
procid => "nVidia Corporation nForce2 Ultra 400 SMBus (MCP)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x00D4,
|
|
procid => "nVidia Corporation nForce3 Pro150 SMBus (MCP)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x00E4,
|
|
procid => "nVidia Corporation nForce3 250Gb SMBus (MCP)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0052,
|
|
procid => "nVidia Corporation nForce4 SMBus (MCP)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0034,
|
|
procid => "nVidia Corporation nForce4 SMBus (MCP-04)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0264,
|
|
procid => "nVidia Corporation nForce SMBus (MCP51)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0368,
|
|
procid => "nVidia Corporation nForce SMBus (MCP55)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x03eb,
|
|
procid => "nVidia Corporation nForce SMBus (MCP61)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0446,
|
|
procid => "nVidia Corporation nForce SMBus (MCP65)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0542,
|
|
procid => "nVidia Corporation nForce SMBus (MCP67)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x07d8,
|
|
procid => "nVidia Corporation nForce SMBus (MCP73)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0752,
|
|
procid => "nVidia Corporation nForce SMBus (MCP78S)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x10de,
|
|
devid => 0x0aa2,
|
|
procid => "nVidia Corporation nForce SMBus (MCP79)",
|
|
driver => "i2c-nforce2",
|
|
}, {
|
|
vendid => 0x1166,
|
|
devid => 0x0200,
|
|
procid => "ServerWorks OSB4 South Bridge",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1055,
|
|
devid => 0x9463,
|
|
procid => "SMSC Victory66 South Bridge",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1166,
|
|
devid => 0x0201,
|
|
procid => "ServerWorks CSB5 South Bridge",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1166,
|
|
devid => 0x0203,
|
|
procid => "ServerWorks CSB6 South Bridge",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1166,
|
|
devid => 0x0205,
|
|
procid => "ServerWorks HT-1000 South Bridge",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1002,
|
|
devid => 0x4353,
|
|
procid => "ATI Technologies Inc ATI SMBus",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1002,
|
|
devid => 0x4363,
|
|
procid => "ATI Technologies Inc ATI SMBus",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1002,
|
|
devid => 0x4372,
|
|
procid => "ATI Technologies Inc IXP SB400 SMBus Controller",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1002,
|
|
devid => 0x4385,
|
|
procid => "ATI Technologies Inc SB600/SB700/SB800 SMBus",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1022,
|
|
devid => 0x780b,
|
|
procid => "AMD Hudson-2 SMBus",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x1022,
|
|
devid => 0x790b,
|
|
procid => "AMD KERNCZ SMBus",
|
|
driver => "i2c-piix4",
|
|
}, {
|
|
vendid => 0x100B,
|
|
devid => 0x0500,
|
|
procid => "SCx200 Bridge",
|
|
driver => "scx200_acb",
|
|
}, {
|
|
vendid => 0x100B,
|
|
devid => 0x0510,
|
|
procid => "SC1100 Bridge",
|
|
driver => "scx200_acb",
|
|
}, {
|
|
vendid => 0x100B,
|
|
devid => 0x002B,
|
|
procid => "CS5535 ISA bridge",
|
|
driver => "scx200_acb",
|
|
}, {
|
|
vendid => 0x1022,
|
|
devid => 0x2090,
|
|
procid => "CS5536 [Geode companion] ISA",
|
|
driver => "scx200_acb",
|
|
}
|
|
);
|
|
|
|
# Look-up table to find out an I2C bus' driver based on the bus name.
|
|
# The match field should contain a regular expression matching the I2C
|
|
# bus name as it would appear in sysfs.
|
|
# Note that new drivers probably don't need to be added to this table
|
|
# if they bind to their device, as we will be able to get the driver name
|
|
# from sysfs directly.
|
|
use vars qw(@i2c_adapter_names);
|
|
@i2c_adapter_names = (
|
|
{ driver => "i2c-piix4", match => qr/^SMBus PIIX4 adapter at / },
|
|
{ driver => "i2c-i801", match => qr/^SMBus I801 adapter at / },
|
|
{ driver => "i2c-via", match => qr/^VIA i2c/ },
|
|
{ driver => "i2c-viapro", match => qr/^SMBus V(IA|ia) Pro adapter at / },
|
|
{ driver => "i2c-sis5595", match => qr/^SMBus SIS5595 adapter at / },
|
|
{ driver => "i2c-sis630", match => qr/^SMBus SIS630 adapter at / },
|
|
{ driver => "i2c-sis96x", match => qr/^SiS96x SMBus adapter at / },
|
|
{ driver => "i2c-ali15x3", match => qr/^SMBus ALI15X3 adapter at / },
|
|
{ driver => "i2c-ali1535", match => qr/^SMBus ALI1535 adapter at/ },
|
|
{ driver => "i2c-ali1563", match => qr/^SMBus ALi 1563 Adapter @ / },
|
|
{ driver => "i2c-amd756", match => qr/^SMBus (AMD756|AMD766|AMD768|AMD8111|nVidia nForce) adapter at / },
|
|
{ driver => "i2c-amd8111", match => qr/^SMBus2 AMD8111 adapter at / },
|
|
{ driver => "i2c-nforce2", match => qr/^SMBus nForce2 adapter at / },
|
|
{ driver => "scx200_acb", match => qr/^(NatSemi SCx200 ACCESS\.bus|SCx200 ACB\d+|CS553[56] ACB\d+)/ },
|
|
);
|
|
|
|
# This is a list of all recognized I2C and ISA chips.
|
|
# Each entry must have the following fields:
|
|
# name: The full chip name
|
|
# driver: The driver name. Put in exactly:
|
|
# * "to-be-written" if it is not yet available
|
|
# * "use-isa-instead" if no i2c driver will be written
|
|
# i2c_addrs (optional): For I2C chips, the list of I2C addresses to
|
|
# probe.
|
|
# i2c_detect (optional): For I2C chips, the function to call to detect
|
|
# this chip. The function will be passed two parameters: an open file
|
|
# descriptor to access the bus, and the I2C address to probe.
|
|
# isa_addrs (optional): For ISA chips, the list of port addresses to
|
|
# probe.
|
|
# isa_detect (optional): For ISA chips, the function to call to detect
|
|
# this chip. The function will be passed 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 detects whether two entries are the same.
|
|
# The function will be passed three parameters: the ISA address, an
|
|
# open file descriptor to access the I2C bus, 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 { winbond_alias_detect(@_, 0x2b, 0x3d); },
|
|
}, {
|
|
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 { winbond_alias_detect(@_, 0x2b, 0x3d); },
|
|
}, {
|
|
name => "National Semiconductor LM75",
|
|
driver => "lm75",
|
|
i2c_addrs => [0x48..0x4f],
|
|
i2c_detect => sub { lm75_detect(@_, 0); },
|
|
}, {
|
|
name => "National Semiconductor LM75A",
|
|
driver => "lm75",
|
|
i2c_addrs => [0x48..0x4f],
|
|
i2c_detect => sub { lm75_detect(@_, 2); },
|
|
}, {
|
|
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(@_, 0); },
|
|
}, {
|
|
name => "National Semiconductor LM96080",
|
|
driver => "lm80",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { lm80_detect(@_, 1); },
|
|
}, {
|
|
name => "TI / National Semiconductor ADC128D818",
|
|
driver => "adc128d818",
|
|
i2c_addrs => [0x1d, 0x1e, 0x1f, 0x2d, 0x2e, 0x2f],
|
|
i2c_detect => sub { adc128d818_detect(@_); },
|
|
}, {
|
|
name => "National Semiconductor LM85",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 0); },
|
|
}, {
|
|
name => "National Semiconductor LM96000 or PC8374L",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 1); },
|
|
}, {
|
|
name => "Analog Devices ADM1027",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 2); },
|
|
}, {
|
|
name => "Analog Devices ADT7460 or ADT7463",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 3); },
|
|
}, {
|
|
name => "SMSC EMC6D100 or EMC6D101",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 4); },
|
|
}, {
|
|
name => "SMSC EMC6D102",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 5); },
|
|
}, {
|
|
name => "SMSC EMC6D103",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 6); },
|
|
}, {
|
|
name => "SMSC EMC6D103S or EMC2300",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 8); },
|
|
}, {
|
|
name => "SMSC EMC6W201",
|
|
driver => "emc6w201",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { emc6w201_detect(@_); },
|
|
}, {
|
|
name => "Analog Devices ADT7462",
|
|
driver => "adt7462",
|
|
i2c_addrs => [0x5c, 0x58],
|
|
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 => "lm85",
|
|
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 => sub { kernel_version_at_least(2, 6, 33) ? "adt7475" : "adt7473" },
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { adt7473_detect(@_, 0); },
|
|
}, {
|
|
name => "Analog Devices ADT7475",
|
|
driver => "adt7475",
|
|
i2c_addrs => [0x2e],
|
|
i2c_detect => sub { adt7473_detect(@_, 1); },
|
|
}, {
|
|
name => "Analog Devices ADT7476",
|
|
driver => "adt7475",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { adt7467_detect(@_, 1); },
|
|
}, {
|
|
name => "Analog Devices ADT7490",
|
|
driver => "adt7475",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { adt7490_detect(@_); },
|
|
}, {
|
|
name => "Analog Devices ADT7410/ADT7420",
|
|
driver => "adt7410",
|
|
i2c_addrs => [0x48..0x4b],
|
|
i2c_detect => sub { adt7410_detect(@_); },
|
|
}, {
|
|
name => "Analog Devices ADT7411",
|
|
driver => "adt7411",
|
|
i2c_addrs => [0x48, 0x4a, 0x4b],
|
|
i2c_detect => sub { adt7411_detect(@_); },
|
|
}, {
|
|
name => "Andigilog aSC7511",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { andigilog_aSC7511_detect(@_); },
|
|
}, {
|
|
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..0x2e],
|
|
i2c_detect => sub { andigilog_detect(@_, 1); },
|
|
}, {
|
|
name => "Andigilog aSC7621",
|
|
driver => "asc7621",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { andigilog_detect(@_, 2); },
|
|
}, {
|
|
name => "National Semiconductor LM87",
|
|
driver => "lm87",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm87_detect(@_, 0); },
|
|
}, {
|
|
name => "Analog Devices ADM1024",
|
|
driver => "lm87",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm87_detect(@_, 1); },
|
|
}, {
|
|
name => "National Semiconductor LM93",
|
|
driver => "lm93",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm93_detect(@_, 0); },
|
|
}, {
|
|
name => "National Semiconductor LM94 or LM96194",
|
|
driver => "lm93",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm93_detect(@_, 1); },
|
|
}, {
|
|
name => "Winbond W83781D",
|
|
driver => "w83781d",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { w83781d_detect(@_, 0); },
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { w83781d_isa_detect(@_, 0); },
|
|
alias_detect => sub { winbond_alias_detect(@_, 0x2b, 0x3d); },
|
|
}, {
|
|
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 { winbond_alias_detect(@_, 0x2b, 0x3d); },
|
|
}, {
|
|
name => "Winbond W83783S",
|
|
driver => "w83781d",
|
|
i2c_addrs => [0x2d],
|
|
i2c_detect => sub { w83781d_detect(@_, 2); },
|
|
}, {
|
|
name => "Winbond W83791D",
|
|
driver => "w83791d",
|
|
i2c_addrs => [0x2c..0x2f],
|
|
i2c_detect => sub { w83781d_detect(@_, 7); },
|
|
}, {
|
|
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(@_); },
|
|
}, {
|
|
name => "Nuvoton W83795G/ADG",
|
|
driver => "w83795",
|
|
i2c_addrs => [0x2c..0x2f],
|
|
i2c_detect => sub { w83795_detect(@_); },
|
|
}, {
|
|
name => "Nuvoton NCT7802Y",
|
|
driver => "nct7802",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { nct7802_detect(@_); },
|
|
}, {
|
|
name => "Nuvoton NCT7904D",
|
|
driver => "nct7904",
|
|
i2c_addrs => [0x2d..0x2e],
|
|
i2c_detect => sub { nct7904_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/W83667HG/W83677HG",
|
|
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 Mozart-2",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x77],
|
|
i2c_detect => sub { mozart_detect(@_); },
|
|
}, {
|
|
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 => "w83l786ng",
|
|
i2c_addrs => [0x2e, 0x2f],
|
|
i2c_detect => sub { w83l784r_detect(@_, 2); },
|
|
}, {
|
|
name => "Winbond W83L785TS-S",
|
|
driver => "w83l785ts",
|
|
i2c_addrs => [0x2e],
|
|
i2c_detect => sub { w83l784r_detect(@_, 3); },
|
|
}, {
|
|
name => "Genesys Logic GL518SM",
|
|
driver => "gl518sm",
|
|
i2c_addrs => [0x2c, 0x2d],
|
|
i2c_detect => sub { gl518sm_detect(@_, 0); },
|
|
}, {
|
|
name => "Genesys Logic GL520SM",
|
|
driver => "gl520sm",
|
|
i2c_addrs => [0x2c, 0x2d],
|
|
i2c_detect => sub { gl518sm_detect(@_, 1); },
|
|
}, {
|
|
name => "Genesys Logic GL525SM",
|
|
driver => "to-be-written",
|
|
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 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..0x2e],
|
|
i2c_detect => sub { adm1026_detect(@_); },
|
|
}, {
|
|
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 => "Global Mixed-mode Technology G781",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c, 0x4d],
|
|
i2c_detect => sub { lm90_detect(@_, 15); },
|
|
}, {
|
|
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 MAX6639",
|
|
driver => "max6639",
|
|
i2c_addrs => [0x2c, 0x2e, 0x2f],
|
|
i2c_detect => sub { max6639_detect(@_); },
|
|
}, {
|
|
name => "Maxim MAX6642",
|
|
driver => "max6642",
|
|
i2c_addrs => [0x48..0x4f],
|
|
i2c_detect => sub { max6642_detect(@_); },
|
|
}, {
|
|
name => "Maxim MAX6655/MAX6656",
|
|
driver => "max6655",
|
|
i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
|
|
i2c_detect => sub { max6655_detect(@_); },
|
|
}, {
|
|
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 MAX1618",
|
|
driver => "max1619",
|
|
i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
|
|
i2c_detect => sub { max1619_detect(@_, 1); },
|
|
}, {
|
|
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(@_); },
|
|
}, {
|
|
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 MAX6654",
|
|
driver => "to-be-written", # probably lm90
|
|
i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
|
|
i2c_detect => sub { lm90_detect(@_, 4); },
|
|
}, {
|
|
name => "Maxim MAX6690",
|
|
driver => "to-be-written", # probably lm90
|
|
i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
|
|
i2c_detect => sub { lm90_detect(@_, 12); },
|
|
}, {
|
|
name => "Maxim MAX6657/MAX6658/MAX6659",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { max6657_detect(@_); },
|
|
}, {
|
|
name => "Maxim MAX6659",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4d..0x4e], # 0x4c is handled above
|
|
i2c_detect => sub { max6657_detect(@_); },
|
|
}, {
|
|
name => "Maxim MAX6646",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4d],
|
|
i2c_detect => sub { lm90_detect(@_, 6); },
|
|
}, {
|
|
name => "Maxim MAX6647",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4e],
|
|
i2c_detect => sub { lm90_detect(@_, 6); },
|
|
}, {
|
|
name => "Maxim MAX6648/MAX6649/MAX6692",
|
|
driver => "lm90",
|
|
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 { max6680_95_detect(@_, 0); },
|
|
}, {
|
|
name => "Maxim MAX6695/MAX6696",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
|
|
i2c_detect => sub { max6680_95_detect(@_, 1); },
|
|
}, {
|
|
name => "Winbond W83L771W/G",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm90_detect(@_, 8); },
|
|
}, {
|
|
name => "Winbond W83L771AWG/ASG",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm90_detect(@_, 11); },
|
|
}, {
|
|
name => "Texas Instruments TMP400",
|
|
driver => "to-be-written", # tmp401
|
|
i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
|
|
i2c_detect => sub { tmp401_detect(@_, 0); },
|
|
}, {
|
|
name => "Texas Instruments TMP401",
|
|
driver => "tmp401",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { tmp401_detect(@_, 1); },
|
|
}, {
|
|
name => "Texas Instruments TMP411A",
|
|
driver => "tmp401",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { tmp401_detect(@_, 2); },
|
|
}, {
|
|
name => "Texas Instruments TMP411B",
|
|
driver => "tmp401",
|
|
i2c_addrs => [0x4d],
|
|
i2c_detect => sub { tmp401_detect(@_, 3); },
|
|
}, {
|
|
name => "Texas Instruments TMP411C",
|
|
driver => "tmp401",
|
|
i2c_addrs => [0x4e],
|
|
i2c_detect => sub { tmp401_detect(@_, 4); },
|
|
}, {
|
|
name => "Texas Instruments TMP421",
|
|
driver => "tmp421",
|
|
i2c_addrs => [0x1c..0x1f, 0x2a, 0x4c..0x4f],
|
|
i2c_detect => sub { tmp42x_detect(@_, 0); },
|
|
}, {
|
|
name => "Texas Instruments TMP422",
|
|
driver => "tmp421",
|
|
i2c_addrs => [0x4c..0x4f],
|
|
i2c_detect => sub { tmp42x_detect(@_, 1); },
|
|
}, {
|
|
name => "Texas Instruments TMP423",
|
|
driver => "tmp421",
|
|
i2c_addrs => [0x4c, 0x4d],
|
|
i2c_detect => sub { tmp42x_detect(@_, 2); },
|
|
}, {
|
|
name => "Texas Instruments TMP431",
|
|
driver => "tmp401",
|
|
i2c_addrs => [0x4c, 0x4d],
|
|
i2c_detect => sub { tmp401_detect(@_, 5); },
|
|
}, {
|
|
name => "Texas Instruments TMP432",
|
|
driver => "tmp401",
|
|
i2c_addrs => [0x4c, 0x4d],
|
|
i2c_detect => sub { tmp401_detect(@_, 6); },
|
|
}, {
|
|
name => "Texas Instruments TMP435",
|
|
driver => "tmp401",
|
|
i2c_addrs => [0x48..0x4f], # 0x37 not probed
|
|
i2c_detect => sub { tmp401_detect(@_, 7); },
|
|
}, {
|
|
name => "Texas Instruments TMP441",
|
|
driver => "tmp421",
|
|
i2c_addrs => [0x1c..0x1f, 0x2a, 0x4c..0x4f],
|
|
i2c_detect => sub { tmp42x_detect(@_, 3); },
|
|
}, {
|
|
name => "Texas Instruments TMP442",
|
|
driver => "tmp421",
|
|
i2c_addrs => [0x4c, 0x4d],
|
|
i2c_detect => sub { tmp42x_detect(@_, 4); },
|
|
}, {
|
|
name => "Texas Instruments TMP451",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { tmp401_detect(@_, 8); },
|
|
}, {
|
|
name => "Texas Instruments AMC6821",
|
|
driver => "amc6821",
|
|
i2c_addrs => [0x18..0x1a, 0x2c..0x2e, 0x4c..0x4e],
|
|
i2c_detect => sub { amc6821_detect(@_); },
|
|
}, {
|
|
name => "National Semiconductor LM95231",
|
|
driver => "lm95241",
|
|
i2c_addrs => [0x2b, 0x19, 0x2a],
|
|
i2c_detect => sub { lm95231_detect(@_, 0); },
|
|
}, {
|
|
name => "National Semiconductor LM95233",
|
|
driver => "lm95234",
|
|
i2c_addrs => [0x18, 0x2a, 0x2b],
|
|
i2c_detect => sub { lm95231_detect(@_, 5); },
|
|
}, {
|
|
name => "National Semiconductor LM95234",
|
|
driver => "lm95234",
|
|
i2c_addrs => [0x18, 0x4d, 0x4e],
|
|
i2c_detect => sub { lm95231_detect(@_, 3); },
|
|
}, {
|
|
name => "National Semiconductor LM95235",
|
|
driver => "lm95245",
|
|
i2c_addrs => [0x18, 0x29, 0x4c],
|
|
i2c_detect => sub { lm95231_detect(@_, 4); },
|
|
}, {
|
|
name => "National Semiconductor LM95241",
|
|
driver => "lm95241",
|
|
i2c_addrs => [0x2b, 0x19, 0x2a],
|
|
i2c_detect => sub { lm95231_detect(@_, 1); },
|
|
}, {
|
|
name => "National Semiconductor LM95245",
|
|
driver => "lm95245",
|
|
i2c_addrs => [0x18, 0x19, 0x29, 0x4c, 0x4d],
|
|
i2c_detect => sub { lm95231_detect(@_, 2); },
|
|
}, {
|
|
name => "National Semiconductor LM63",
|
|
driver => "lm63",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm63_detect(@_, 1); },
|
|
}, {
|
|
name => "National Semiconductor LM64",
|
|
driver => "lm63",
|
|
i2c_addrs => [0x18, 0x4e],
|
|
i2c_detect => sub { lm63_detect(@_, 3); },
|
|
}, {
|
|
name => "National Semiconductor LM96163",
|
|
driver => "lm63",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm63_detect(@_, 4); },
|
|
}, {
|
|
name => "Fintek F75363SG",
|
|
driver => "lm63", # Not yet
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm63_detect(@_, 2); },
|
|
}, {
|
|
name => "National Semiconductor LM73",
|
|
driver => "lm73",
|
|
i2c_addrs => [0x48..0x4a, 0x4c..0x4e],
|
|
i2c_detect => sub { lm73_detect(@_); },
|
|
}, {
|
|
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 ADT7461A, ON Semiconductor NCT1008",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c..0x4d],
|
|
i2c_detect => sub { lm90_detect(@_, 13); },
|
|
}, {
|
|
name => "NXP/Philips SA56004",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x48..0x4f],
|
|
i2c_detect => sub { lm90_detect(@_, 14); },
|
|
}, {
|
|
name => "Analog Devices ADT7481",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x4c, 0x4b],
|
|
i2c_detect => sub { adt7481_detect(@_); },
|
|
}, {
|
|
name => "Analog Devices ADM1029",
|
|
driver => "adm1029",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { adm1029_detect(@_); },
|
|
}, {
|
|
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 => "Texas Instruments THMC51",
|
|
driver => "to-be-written", # thmc50
|
|
i2c_addrs => [0x2e], # At least (no datasheet)
|
|
i2c_detect => sub { adm1022_detect(@_, 3); },
|
|
}, {
|
|
name => "VIA VT1211 (I2C)",
|
|
driver => "use-isa-instead",
|
|
i2c_addrs => [0x2d],
|
|
i2c_detect => sub { vt1211_i2c_detect(@_); },
|
|
}, {
|
|
name => "ITE IT8712F",
|
|
driver => "it87",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { it8712_i2c_detect(@_); },
|
|
}, {
|
|
name => "FSC Poseidon I",
|
|
driver => sub { kernel_version_at_least(2, 6, 24) ? "fschmd" : "fscpos" },
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fsc_detect(@_, 0); },
|
|
}, {
|
|
name => "FSC Poseidon II",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fsc_detect(@_, 1); },
|
|
}, {
|
|
name => "FSC Scylla",
|
|
driver => "fschmd",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fsc_detect(@_, 2); },
|
|
}, {
|
|
name => "FSC Hermes",
|
|
driver => sub { kernel_version_at_least(2, 6, 24) ? "fschmd" : "fscher" },
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fsc_detect(@_, 3); },
|
|
}, {
|
|
name => "FSC Heimdal",
|
|
driver => "fschmd",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fsc_detect(@_, 4); },
|
|
}, {
|
|
name => "FSC Heracles",
|
|
driver => "fschmd",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fsc_detect(@_, 5); },
|
|
}, {
|
|
name => "FSC Hades",
|
|
driver => "fschmd",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fsc_detect(@_, 6); },
|
|
}, {
|
|
name => "FSC Syleus",
|
|
driver => "fschmd",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fsc_detect(@_, 7); },
|
|
}, {
|
|
name => "FTS Teutates",
|
|
driver => "ftsteutates",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fts_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 => "SMSC EMC2103",
|
|
driver => "emc2103",
|
|
i2c_addrs => [0x2e],
|
|
i2c_detect => sub { emc1403_detect(@_, 2); },
|
|
}, {
|
|
name => "SMSC EMC2104",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x2f],
|
|
i2c_detect => sub { emc1403_detect(@_, 13); },
|
|
}, {
|
|
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 => "f75375s",
|
|
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 => "SMSC EMC1002",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x4c, 0x4d], # 0x3c, 0x3d not probed
|
|
i2c_detect => sub { emc1403_detect(@_, 4); },
|
|
}, {
|
|
name => "SMSC EMC1023",
|
|
driver => "to-be-written", # emc1023
|
|
i2c_addrs => [0x48, 0x49, 0x4c, 0x4d],
|
|
i2c_detect => sub { emc1023_detect(@_, 0); },
|
|
}, {
|
|
name => "SMSC EMC1033",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x4c, 0x4d], # 0x3c, 0x3d not probed
|
|
i2c_detect => sub { emc1403_detect(@_, 5); },
|
|
}, {
|
|
name => "SMSC EMC1043",
|
|
driver => "to-be-written", # emc1023
|
|
i2c_addrs => [0x48, 0x49, 0x4c, 0x4d],
|
|
i2c_detect => sub { emc1023_detect(@_, 1); },
|
|
}, {
|
|
name => "SMSC EMC1046",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x4c, 0x4d],
|
|
i2c_detect => sub { emc1403_detect(@_, 6); },
|
|
}, {
|
|
name => "SMSC EMC1047",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x18], # 0x10 not probed
|
|
i2c_detect => sub { emc1403_detect(@_, 7); },
|
|
}, {
|
|
name => "SMSC EMC1053",
|
|
driver => "to-be-written", # emc1023
|
|
i2c_addrs => [0x48, 0x49, 0x4c, 0x4d],
|
|
i2c_detect => sub { emc1023_detect(@_, 2); },
|
|
}, {
|
|
name => "SMSC EMC1063",
|
|
driver => "to-be-written", # emc1023
|
|
i2c_addrs => [0x48, 0x49, 0x4c, 0x4d],
|
|
i2c_detect => sub { emc1023_detect(@_, 3); },
|
|
}, {
|
|
name => "SMSC EMC1072",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x1c, 0x2c, 0x4c, 0x5c], # 0x3c, 0x6c, 0x7c not probed
|
|
i2c_detect => sub { emc1403_detect(@_, 8); },
|
|
}, {
|
|
name => "SMSC EMC1073",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x1c, 0x2c, 0x4c, 0x5c], # 0x3c, 0x6c, 0x7c not probed
|
|
i2c_detect => sub { emc1403_detect(@_, 9); },
|
|
}, {
|
|
name => "SMSC EMC1074",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x1c, 0x2c, 0x4c, 0x5c], # 0x3c, 0x6c, 0x7c not probed
|
|
i2c_detect => sub { emc1403_detect(@_, 10); },
|
|
}, {
|
|
name => "SMSC EMC1402",
|
|
driver => "emc1403",
|
|
i2c_addrs => [0x18, 0x29, 0x4c, 0x4d],
|
|
i2c_detect => sub { emc1403_detect(@_, 11); },
|
|
}, {
|
|
name => "SMSC EMC1403",
|
|
driver => "emc1403",
|
|
i2c_addrs => [0x18, 0x29, 0x4c, 0x4d],
|
|
i2c_detect => sub { emc1403_detect(@_, 0); },
|
|
}, {
|
|
name => "SMSC EMC1404",
|
|
driver => "emc1403",
|
|
i2c_addrs => [0x18, 0x29, 0x4c, 0x4d],
|
|
i2c_detect => sub { emc1403_detect(@_, 1); },
|
|
}, {
|
|
name => "SMSC EMC1422",
|
|
driver => "emc1403",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { emc1403_detect(@_, 14); },
|
|
}, {
|
|
name => "SMSC EMC1423",
|
|
driver => "emc1403",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { emc1403_detect(@_, 3); },
|
|
}, {
|
|
name => "SMSC EMC1424",
|
|
driver => "emc1403",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { emc1403_detect(@_, 12); },
|
|
}, {
|
|
name => "ST STTS424",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 0); },
|
|
}, {
|
|
name => "ST STTS424E",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 10); },
|
|
}, {
|
|
name => "ST STTS2002",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 11); },
|
|
}, {
|
|
name => "ST STTS3000",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 12); },
|
|
}, {
|
|
name => "NXP SE97/SE97B",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 1); },
|
|
}, {
|
|
name => "NXP SE98",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 2); },
|
|
}, {
|
|
name => "Analog Devices ADT7408",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 3); },
|
|
}, {
|
|
name => "IDT TS3000/TSE2002",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 4); },
|
|
}, {
|
|
name => "IDT TSE2004",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 16); },
|
|
}, {
|
|
name => "IDT TS3001",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 17); },
|
|
}, {
|
|
name => "Maxim MAX6604",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 5); },
|
|
}, {
|
|
name => "Microchip MCP9804",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 13); },
|
|
}, {
|
|
name => "Microchip MCP9808",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 18); },
|
|
},{
|
|
name => "Microchip MCP98242",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 6); },
|
|
}, {
|
|
name => "Microchip MCP98243",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 7); },
|
|
}, {
|
|
name => "Microchip MCP98244",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 15); },
|
|
}, {
|
|
name => "Microchip MCP9843",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 8); },
|
|
}, {
|
|
name => "ON CAT6095/CAT34TS02",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 9); },
|
|
}, {
|
|
name => "ON CAT34TS02C",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 19); },
|
|
}, {
|
|
name => "ON CAT34TS04",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 21); },
|
|
}, {
|
|
name => "Atmel AT30TS00",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 14); },
|
|
}, {
|
|
name => "Giantec GT30TS00",
|
|
driver => "jc42",
|
|
i2c_addrs => [0x18..0x1f],
|
|
i2c_detect => sub { jedec_JC42_4_detect(@_, 20); },
|
|
}
|
|
);
|
|
|
|
# IPMI interfaces have their own array now
|
|
@ipmi_ifs = (
|
|
{
|
|
name => "IPMI BMC KCS",
|
|
driver => "to-be-written", # ipmisensors
|
|
isa_addrs => [0x0ca0],
|
|
isa_detect => sub { ipmi_detect(@_); },
|
|
}, {
|
|
name => "IPMI BMC SMIC",
|
|
driver => "to-be-written", # ipmisensors
|
|
isa_addrs => [0x0ca8],
|
|
isa_detect => sub { ipmi_detect(@_); },
|
|
}
|
|
);
|
|
|
|
# Here is a similar list, but for devices which are not hardware monitoring
|
|
# chips. We only list popular devices which happen to live at the same I2C
|
|
# address as recognized hardware monitoring chips. The idea is to make it
|
|
# clear that the chip in question is of no interest for lm-sensors.
|
|
@non_hwmon_chip_ids = (
|
|
{
|
|
name => "Winbond W83791SD",
|
|
i2c_addrs => [0x2c..0x2f],
|
|
i2c_detect => sub { w83791sd_detect(@_); },
|
|
}, {
|
|
name => "Winbond WPCD377I",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect(@_, 7); },
|
|
}, {
|
|
name => "Fintek F75111R/RG/N (GPIO)",
|
|
i2c_addrs => [0x37, 0x4e],
|
|
i2c_detect => sub { fintek_detect(@_, 1); },
|
|
}, {
|
|
name => "ITE IT8201R/IT8203R/IT8206R/IT8266R",
|
|
i2c_addrs => [0x4e],
|
|
i2c_detect => sub { ite_overclock_detect(@_); },
|
|
}, {
|
|
name => "SPD EEPROM",
|
|
i2c_addrs => [0x50..0x57],
|
|
i2c_detect => sub { eeprom_detect(@_); },
|
|
}, {
|
|
name => "EDID EEPROM",
|
|
i2c_addrs => [0x50],
|
|
i2c_detect => sub { ddcmonitor_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. Put in exactly:
|
|
# * "to-be-written" if it is not yet available
|
|
# * "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)
|
|
# * "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 we have to match (base device)
|
|
# devid_mask (optional): Bitmask to apply before checking the device ID
|
|
# regs (optional): Register definitions, where they differ from the standard.
|
|
# logdev: The logical device containing the sensors
|
|
# check (optional): A function to refine the detection. Will be passed
|
|
# the index and data ports as parameters. Must return 1 for a matching
|
|
# device, 0 otherwise.
|
|
# features (optional): Features supported by this device, amongst:
|
|
# * FEAT_IN
|
|
# * FEAT_FAN
|
|
# * FEAT_TEMP
|
|
use vars qw(@superio_ids_natsemi @superio_ids_smsc @superio_ids_smsc_ns
|
|
@superio_ids_winbond @superio_ids_ite @superio_ids);
|
|
|
|
use constant FEAT_IN => (1 << 0);
|
|
use constant FEAT_FAN => (1 << 1);
|
|
use constant FEAT_TEMP => (1 << 2);
|
|
use constant FEAT_SMBUS => (1 << 7);
|
|
|
|
@superio_ids_natsemi = (
|
|
{
|
|
name => "Nat. Semi. PC8374L Super IO Sensors", # Also Nuvoton WPCD374L
|
|
driver => "to-be-written",
|
|
devid => 0xf1,
|
|
check => sub {
|
|
outb($_[0], 0x27);
|
|
# Guess work; seen so far: 0x11
|
|
return (inb($_[1]) < 0x80);
|
|
},
|
|
logdev => 0x08,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton WPCD377I Super IO", # Also WPCD376I
|
|
driver => "not-a-sensor",
|
|
devid => 0xf1,
|
|
check => sub {
|
|
outb($_[0], 0x27);
|
|
# Guess work; seen so far: 0x91 (twice)
|
|
return (inb($_[1]) >= 0x80);
|
|
},
|
|
}, {
|
|
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,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "Nat. Semi. PC87363 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe8,
|
|
logdev => 0x09,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "Nat. Semi. PC87364 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe4,
|
|
logdev => 0x09,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "Nat. Semi. PC87365 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe5,
|
|
logdev => 0x09,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "Nat. Semi. PC87365 Super IO Voltage Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe5,
|
|
logdev => 0x0d,
|
|
features => FEAT_IN,
|
|
}, {
|
|
name => "Nat. Semi. PC87365 Super IO Thermal Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe5,
|
|
logdev => 0x0e,
|
|
features => FEAT_TEMP,
|
|
}, {
|
|
name => "Nat. Semi. PC87366 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe9,
|
|
logdev => 0x09,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "Nat. Semi. PC87366 Super IO Voltage Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe9,
|
|
logdev => 0x0d,
|
|
features => FEAT_IN,
|
|
}, {
|
|
name => "Nat. Semi. PC87366 Super IO Thermal Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe9,
|
|
logdev => 0x0e,
|
|
features => FEAT_TEMP,
|
|
}, {
|
|
name => "Nat. Semi. PC87372 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0xf0,
|
|
logdev => 0x09,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "Nat. Semi. PC87373 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0xf3,
|
|
logdev => 0x09,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
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,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "Nat. Semi. PC87427 Super IO Health Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0xf2,
|
|
logdev => 0x14,
|
|
features => FEAT_IN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8510E/TE/G Super IO",
|
|
driver => "to-be-written",
|
|
devid => 0x8510,
|
|
features => FEAT_IN | FEAT_FAN,
|
|
}, {
|
|
name => "ITE IT8511E/TE/G Super IO",
|
|
driver => "to-be-written",
|
|
devid => 0x8511,
|
|
features => FEAT_IN | FEAT_FAN,
|
|
}, {
|
|
name => "ITE IT8512E/F/G Super IO",
|
|
driver => "to-be-written",
|
|
devid => 0x8512,
|
|
features => FEAT_IN | FEAT_FAN,
|
|
}, {
|
|
name => "ITE IT8513E/F/G Super IO",
|
|
driver => "to-be-written",
|
|
devid => 0x8513,
|
|
features => FEAT_IN | FEAT_FAN,
|
|
}, {
|
|
name => "ITE IT8516E/F/G Super IO",
|
|
driver => "to-be-written",
|
|
devid => 0x8516,
|
|
features => FEAT_IN | FEAT_FAN,
|
|
}, {
|
|
name => "ITE IT8518E Super IO",
|
|
driver => "to-be-written",
|
|
devid => 0x8518,
|
|
features => FEAT_IN | FEAT_FAN,
|
|
}
|
|
);
|
|
|
|
@superio_ids_smsc = (
|
|
{
|
|
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 EMC2700LPC Super IO",
|
|
# no datasheet
|
|
devid => 0x67,
|
|
}, {
|
|
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,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
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,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47B397-NC Super IO",
|
|
driver => "smsc47b397",
|
|
devid => 0x6f,
|
|
logdev => 0x08,
|
|
features => FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "SMSC LPC47M10x/112/13x Super IO Fan Sensors",
|
|
driver => "smsc47m1",
|
|
devid => 0x59,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47M14x Super IO Fan Sensors",
|
|
driver => "smsc47m1",
|
|
devid => 0x5f,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47M15x/192/997 Super IO Fan Sensors",
|
|
driver => "smsc47m1",
|
|
devid => 0x60,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47M172 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x14,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47M182 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x74,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47M233 Super IO Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x6b80,
|
|
devid_mask => 0xff80,
|
|
logdev => 0x0a,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "SMSC LPC47M292 Super IO Fan Sensors",
|
|
driver => "smsc47m1",
|
|
devid => 0x6b00,
|
|
devid_mask => 0xff80,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47M584-NC Super IO",
|
|
# No datasheet (Dell-specific part). Not compatible with
|
|
# smsc47m1 nor dme1737 nor LPC47M233. No evidence that this
|
|
# chip can do any hardware monitoring at all.
|
|
driver => "not-a-sensor",
|
|
devid => 0x76,
|
|
}, {
|
|
name => "SMSC LPC47N252 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x0e,
|
|
logdev => 0x09,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47S42x Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x57,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47S45x Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x62,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47U32x Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x58,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC LPC47U33x Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x54,
|
|
logdev => 0x0a,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "SMSC SCH3112 Super IO",
|
|
driver => "dme1737",
|
|
devid => 0x7c,
|
|
logdev => 0x0a,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "SMSC SCH3114 Super IO",
|
|
driver => "dme1737",
|
|
devid => 0x7d,
|
|
logdev => 0x0a,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "SMSC SCH3116 Super IO",
|
|
driver => "dme1737",
|
|
devid => 0x7f,
|
|
logdev => 0x0a,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "SMSC SCH4307 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x90,
|
|
logdev => 0x08,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
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,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "SMSC SCH5307-NS Super IO",
|
|
driver => "smsc47b397",
|
|
devid => 0x81,
|
|
logdev => 0x08,
|
|
features => FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "SMSC SCH5317 Super IO",
|
|
driver => "smsc47b397",
|
|
devid => 0x85,
|
|
logdev => 0x08,
|
|
features => FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
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,
|
|
features => FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
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,
|
|
}, {
|
|
name => "SMSC SCH5627 Super IO",
|
|
driver => "sch5627",
|
|
devid => 0xc6,
|
|
regs => {
|
|
basereg_lsb => 0x66,
|
|
basereg_msb => 0x67,
|
|
},
|
|
logdev => 0x0c,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "SMSC SCH5636 Super IO",
|
|
driver => "sch5636",
|
|
devid => 0xc7,
|
|
regs => {
|
|
basereg_lsb => 0x66,
|
|
basereg_msb => 0x67,
|
|
},
|
|
logdev => 0x0c,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}
|
|
);
|
|
|
|
# Non-standard SMSC 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).
|
|
@superio_ids_smsc_ns = (
|
|
{
|
|
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 LPC47N217 Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x7a,
|
|
}, {
|
|
name => "SMSC LPC47N227 Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x5a,
|
|
}, {
|
|
name => "SMSC LPC47N237 Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x13,
|
|
}, {
|
|
name => "SMSC SIO10N268 Notebook IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x5b,
|
|
}
|
|
);
|
|
|
|
@superio_ids_winbond = (
|
|
{
|
|
name => "VIA VT1211 Super IO Sensors",
|
|
driver => "vt1211",
|
|
devid => 0x3c,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "VIA VT1212 Super IO Lite", # in 100 pin TQFP package
|
|
driver => "not-a-sensor",
|
|
devid => 0x3e,
|
|
}, {
|
|
name => "VIA VT1212 Super IO Lite", # in 48 pin LQFP package
|
|
driver => "not-a-sensor",
|
|
devid => 0x3f,
|
|
}, {
|
|
name => "Winbond W83627HF/F/HG/G Super IO Sensors",
|
|
# Also SMSC LPC61W492
|
|
driver => "w83627hf",
|
|
devid => 0x52,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83627SF/GF Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x59,
|
|
}, {
|
|
name => "Winbond W83627THF/THG Super IO Sensors",
|
|
driver => "w83627hf",
|
|
devid => 0x82,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83637HF/HG Super IO Sensors",
|
|
driver => "w83627hf",
|
|
devid => 0x70,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83687THF Super IO Sensors",
|
|
driver => "w83627hf",
|
|
devid => 0x85,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83697HF/F/HG Super IO Sensors",
|
|
driver => "w83627hf",
|
|
devid => 0x60,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83697SF/UF/UG Super IO PWM",
|
|
driver => "to-be-written",
|
|
devid => 0x68,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
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,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83627DHG Super IO Sensors",
|
|
driver => "w83627ehf",
|
|
devid => 0xA020,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83627DHG-P/W83527HG Super IO Sensors",
|
|
driver => "w83627ehf",
|
|
devid => 0xB070,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83627UHG/NCT6627UD Super IO Sensors",
|
|
driver => "w83627ehf",
|
|
devid => 0xA230,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83667HG Super IO Sensors",
|
|
driver => "w83627ehf",
|
|
devid => 0xA510,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6681D/NCT6682D eSIO",
|
|
driver => "to-be-written",
|
|
devid => 0xB270,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton W83667HG-B (NCT5571D) Super IO Sensors",
|
|
driver => "w83627ehf",
|
|
devid => 0xB350,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton W83677HG-I (NCT5572D/NCT6771F/NCT6772F/NCT6775F) Super IO Sensors",
|
|
driver => sub { kernel_version_at_least(3, 10, 0) ? "nct6775" : "w83627ehf" },
|
|
devid => 0xB470,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Winbond W83L517D Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x61,
|
|
}, {
|
|
name => "Nuvoton NCT6683D eSIO",
|
|
driver => "nct6683",
|
|
devid => 0xC730,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6687D eSIO",
|
|
driver => "nct6683",
|
|
devid => 0xD590,
|
|
devid_mask => 0xFFF0,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6102D/NCT6104D/NCT6106D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xC450,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6112D/NCT6114D/NCT6116D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xD280,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT5573D/NCT5577D/NCT6776F Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xC330,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT5532D/NCT6779D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xC560,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6791D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xC800,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6792D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xC910,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6793D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xD120,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6795D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xD350,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6796D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xD420,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6797D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xD450,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Nuvoton NCT6798D Super IO Sensors",
|
|
driver => "nct6775",
|
|
devid => 0xD428,
|
|
devid_mask => 0xFFF8,
|
|
logdev => 0x0b,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71805F/FG Super IO Sensors",
|
|
driver => "f71805f",
|
|
devid => 0x0406,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71808E Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x0901,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71808A Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x1001,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71862FG Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x0601,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71868A Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x1106,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71869F/E Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x0814,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71869A Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x1007,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71806FG/F71872FG Super IO Sensors",
|
|
driver => "f71805f",
|
|
devid => 0x0341,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71858DG Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x0507,
|
|
logdev => 0x02,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71882FG/F71883FG Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x0541,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71889FG/F81801U Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x0723,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71889ED Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x0909,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F71889A Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x1005,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F81216D Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x0208,
|
|
}, {
|
|
name => "Fintek F81218D Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x0206,
|
|
}, {
|
|
name => "Fintek F81768D Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x1210,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F81865F Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x0704,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Fintek F81866D Super IO Sensors",
|
|
driver => "f71882fg",
|
|
devid => 0x1010,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "Asus F8000 Super IO",
|
|
driver => "f71882fg",
|
|
devid => 0x0581,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
# Shouldn't be in this family, but seems to be still.
|
|
name => "ITE IT8708F Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x8708,
|
|
}, {
|
|
# Shouldn't be in this family, but seems to be still.
|
|
name => "ITE IT8710F Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x8710,
|
|
}
|
|
);
|
|
|
|
@superio_ids_ite = (
|
|
{
|
|
name => "ITE IT8603E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8603,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8606E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8606,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8607E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8607,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8613E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8613,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8620E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8620,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8622E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8622,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8623E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8623,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8625E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8625,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8626E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8626,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8655E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8655,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8665E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8665,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8686E Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8686,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8702F Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x8702,
|
|
logdev => 0x04,
|
|
features => FEAT_FAN,
|
|
}, {
|
|
name => "ITE IT8705F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8705,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8712F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8712,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8716F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8716,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8718F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8718,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8720F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8720,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8721F/IT8758E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8721,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8726F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8726,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8728F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8728,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8731F Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8731,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8732F Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8732,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8752F Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8752,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8771E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8771,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8772E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8772,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8780F Super IO Sensors",
|
|
driver => "to-be-written", # it87
|
|
devid => 0x8780,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8781F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8781,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8782F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8782,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8783F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8783,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8786E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8786,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8790E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8790,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8792E Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8733,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}, {
|
|
name => "ITE IT8987D Embedded Controller",
|
|
driver => "to-be-written",
|
|
devid => 0x8987,
|
|
logdev => 0x04,
|
|
features => FEAT_IN | FEAT_FAN | FEAT_TEMP,
|
|
}
|
|
);
|
|
|
|
# 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/ITE",
|
|
enter =>
|
|
{
|
|
0x2e => [],
|
|
0x4e => [],
|
|
},
|
|
chips => \@superio_ids_natsemi,
|
|
}, {
|
|
family => "SMSC",
|
|
enter =>
|
|
{
|
|
0x2e => [0x55],
|
|
0x4e => [0x55],
|
|
},
|
|
chips => \@superio_ids_smsc,
|
|
ns_detect => \&smsc_ns_detect_superio,
|
|
ns_chips => \@superio_ids_smsc_ns,
|
|
}, {
|
|
family => "VIA/Winbond/Nuvoton/Fintek",
|
|
guess => 0x290,
|
|
enter =>
|
|
{
|
|
0x2e => [0x87, 0x87],
|
|
0x4e => [0x87, 0x87],
|
|
},
|
|
chips => \@superio_ids_winbond,
|
|
}, {
|
|
family => "ITE",
|
|
guess => 0x290,
|
|
enter =>
|
|
{
|
|
0x2e => [0x87, 0x01, 0x55, 0x55],
|
|
0x4e => [0x87, 0x01, 0x55, 0xaa],
|
|
},
|
|
chips => \@superio_ids_ite,
|
|
}
|
|
);
|
|
|
|
# Drivers for bridge, CPU and memory embedded sensors
|
|
# Each entry must have the following fields:
|
|
# name: The device 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.
|
|
use vars qw(@cpu_ids);
|
|
|
|
@cpu_ids = (
|
|
{
|
|
name => "Silicon Integrated Systems SIS5595",
|
|
driver => "sis5595",
|
|
detect => \&sis5595_pci_detect,
|
|
}, {
|
|
name => "VIA VT82C686 Integrated Sensors",
|
|
driver => "via686a",
|
|
detect => \&via686a_pci_detect,
|
|
}, {
|
|
name => "VIA VT8231 Integrated Sensors",
|
|
driver => "vt8231",
|
|
detect => \&via8231_pci_detect,
|
|
}, {
|
|
name => "AMD K8 thermal sensors",
|
|
driver => "k8temp",
|
|
detect => sub { amd_pci_detect('1103') },
|
|
}, {
|
|
name => "AMD Family 10h thermal sensors",
|
|
driver => "k10temp",
|
|
detect => \&fam10h_pci_detect,
|
|
}, {
|
|
name => "AMD Family 11h thermal sensors",
|
|
driver => "k10temp",
|
|
detect => sub { amd_pci_detect('1303') },
|
|
}, {
|
|
name => "AMD Family 12h and 14h thermal sensors",
|
|
driver => "k10temp",
|
|
detect => sub { amd_pci_detect('1703') },
|
|
}, {
|
|
name => "AMD Family 15h thermal sensors",
|
|
driver => "k10temp",
|
|
detect => sub { amd_pci_detect('1603', '1403', '141d', '1573', '15b3') },
|
|
}, {
|
|
name => "AMD Family 16h thermal sensors",
|
|
driver => "k10temp",
|
|
detect => sub { amd_pci_detect('1533', '1583') },
|
|
}, {
|
|
name => "AMD Family 17h thermal sensors",
|
|
driver => "k10temp",
|
|
detect => sub { amd_pci_detect('1463', '15d0', '1493', '1443') },
|
|
}, {
|
|
name => "AMD Family 15h power sensors",
|
|
driver => "fam15h_power",
|
|
detect => sub {
|
|
amd_pci_detect('1604', '141e', '1574', '15b4')
|
|
},
|
|
}, {
|
|
name => "AMD Family 16h power sensors",
|
|
driver => "fam15h_power",
|
|
detect => sub { amd_pci_detect('1534', '1584') },
|
|
}, {
|
|
name => "Hygon Family 18h thermal sensors",
|
|
driver => "k10temp",
|
|
detect => sub { hygon_pci_detect('1463') },
|
|
}, {
|
|
name => "AMD Family 19h thermal sensors",
|
|
driver => "k10temp",
|
|
detect => sub { amd_pci_detect('1653') },
|
|
}, {
|
|
name => "Intel digital thermal sensor",
|
|
driver => "coretemp",
|
|
detect => \&coretemp_detect,
|
|
}, {
|
|
name => "Intel AMB FB-DIMM thermal sensor",
|
|
driver => "i5k_amb",
|
|
detect => \&intel_amb_detect,
|
|
}, {
|
|
name => "Intel 5500/5520/X58 thermal sensor",
|
|
driver => "i5500_temp",
|
|
detect => \&intel_5500_detect,
|
|
}, {
|
|
name => "VIA C7 thermal sensor",
|
|
driver => "via-cputemp",
|
|
detect => \&via_c7_detect,
|
|
}, {
|
|
name => "VIA Nano thermal sensor",
|
|
driver => "via-cputemp",
|
|
detect => \&via_nano_detect,
|
|
}
|
|
);
|
|
|
|
#######################
|
|
# AUXILIARY FUNCTIONS #
|
|
#######################
|
|
|
|
# $_[0] is the sought value
|
|
# $_[1..] is the list to seek in
|
|
# Returns: 1 if found, 0 if not.
|
|
sub contains
|
|
{
|
|
my $sought = shift;
|
|
local $_;
|
|
|
|
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 =~ m/^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;
|
|
}
|
|
|
|
# $_[0]: Reference to base hash
|
|
# $_[1]: Reference to overlay hash
|
|
# Result: Overlayed hash
|
|
sub overlay_hash
|
|
{
|
|
my ($base, $overlay) = @_;
|
|
my %result = %{$base};
|
|
|
|
foreach my $key (keys %{$overlay}) {
|
|
$result{$key} = $overlay->{$key};
|
|
}
|
|
return %result;
|
|
}
|
|
|
|
# Read answer from stdin or accept default answer automatically
|
|
sub read_answer
|
|
{
|
|
if ($opt{auto}) {
|
|
print "\n";
|
|
return "";
|
|
}
|
|
return <STDIN> || "";
|
|
}
|
|
|
|
###################
|
|
# I/O PORT ACCESS #
|
|
###################
|
|
|
|
sub initialize_ioports
|
|
{
|
|
if (sysopen(IOPORTS, "/dev/port", O_RDWR)) {
|
|
binmode(IOPORTS);
|
|
return 1;
|
|
}
|
|
print STDERR "/dev/port: $!\n";
|
|
return 0;
|
|
}
|
|
|
|
sub close_ioports
|
|
{
|
|
close(IOPORTS);
|
|
}
|
|
|
|
# $_[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
|
|
# We assume this can't fail.
|
|
sub outb
|
|
{
|
|
sysseek(IOPORTS, $_[0], 0);
|
|
syswrite(IOPORTS, pack("C", $_[1]), 1);
|
|
}
|
|
|
|
# $_[0]: Address register
|
|
# $_[1]: Data register
|
|
# $_[2]: Register to read
|
|
# Returns: read value
|
|
sub isa_read_byte
|
|
{
|
|
outb($_[0], $_[2]);
|
|
return inb($_[1]);
|
|
}
|
|
|
|
# $_[0]: Base address
|
|
# $_[1]: Register to read
|
|
# Returns: read value
|
|
# This one can be used for any ISA chip with index register at
|
|
# offset 5 and data register at offset 6.
|
|
sub isa_read_i5d6
|
|
{
|
|
my ($addr, $reg) = @_;
|
|
return isa_read_byte($addr + 5, $addr + 6, $reg);
|
|
}
|
|
|
|
#################
|
|
# AUTODETECTION #
|
|
#################
|
|
|
|
use vars qw($dev_i2c $sysfs_root $systemd_systemctl $systemd_system_dir);
|
|
|
|
sub initialize_conf
|
|
{
|
|
my $use_devfs = 0;
|
|
open(local *INPUTFILE, "/proc/mounts") or die "Can't access /proc/mounts!";
|
|
local $_;
|
|
while (<INPUTFILE>) {
|
|
if (m@^\w+ /dev devfs @) {
|
|
$use_devfs = 1;
|
|
$dev_i2c = '/dev/i2c/';
|
|
}
|
|
if (m@^\S+ (/\w+) sysfs @) {
|
|
$sysfs_root = $1;
|
|
}
|
|
}
|
|
close(INPUTFILE);
|
|
|
|
# We need sysfs for many things
|
|
if (!defined $sysfs_root) {
|
|
print "Sysfs not mounted?\n";
|
|
exit -1;
|
|
}
|
|
|
|
my $use_udev = 0;
|
|
if (open(*INPUTFILE, '/etc/udev/udev.conf')) {
|
|
while (<INPUTFILE>) {
|
|
next unless 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' || -e '/run/udev') {
|
|
$use_udev = 1;
|
|
$dev_i2c = '/dev/i2c-';
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
# Detect systemd presence and paths
|
|
if (-x "/usr/bin/systemctl") {
|
|
$systemd_systemctl = "/usr/bin/systemctl";
|
|
} elsif (-x "/bin/systemctl") {
|
|
$systemd_systemctl = "/bin/systemctl";
|
|
}
|
|
|
|
if (-d "/usr/lib/systemd/system") {
|
|
$systemd_system_dir = "/usr/lib/systemd/system";
|
|
} elsif (-d "/lib/systemd/system") {
|
|
$systemd_system_dir = "/lib/systemd/system";
|
|
}
|
|
}
|
|
|
|
# [0] -> VERSION
|
|
# [1] -> PATCHLEVEL
|
|
# [2] -> SUBLEVEL
|
|
# [3] -> EXTRAVERSION
|
|
#
|
|
use vars qw($kernel_version @kernel_version $kernel_arch);
|
|
|
|
sub initialize_kernel_version
|
|
{
|
|
chomp($kernel_version = `uname -r`);
|
|
$kernel_version =~ /(\d+)\.(\d+)\.(\d+)(.*)/;
|
|
@kernel_version = ($1, $2, $3, $4);
|
|
chomp($kernel_arch = `uname -m`);
|
|
|
|
# We only support kernels >= 2.6.5
|
|
if (!kernel_version_at_least(2, 6, 5)) {
|
|
print "Kernel version is unsupported (too old, >= 2.6.5 needed)\n";
|
|
exit -1;
|
|
}
|
|
}
|
|
|
|
sub print_kernel_version
|
|
{
|
|
printf "# Kernel: \%d.\%d.\%d\%s \%s\n", @kernel_version, $kernel_arch;
|
|
}
|
|
|
|
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
|
|
{
|
|
local $_;
|
|
my $entry;
|
|
|
|
open(local *INPUTFILE, "/proc/cpuinfo") or die "Can't access /proc/cpuinfo!";
|
|
while (<INPUTFILE>) {
|
|
if (m/^processor\s*:\s*(\d+)/) {
|
|
push @cpu, $entry if scalar keys(%{$entry}); # Previous entry
|
|
$entry = { # New entry
|
|
nr => $1,
|
|
vendor_id => "undefined",
|
|
};
|
|
next;
|
|
}
|
|
if (m/^(vendor_id|cpu family|model|model name|stepping|cpuid level|cpu|revision)\s*:\s*(.+)$/) {
|
|
my $k = $1;
|
|
my $v = $2;
|
|
$v =~ s/\s+/ /g; # Merge multiple spaces
|
|
$v =~ s/ $//; # Trim trailing space
|
|
$entry->{$k} = $v;
|
|
next;
|
|
}
|
|
}
|
|
close(INPUTFILE);
|
|
push @cpu, $entry if scalar keys(%{$entry}); # Last entry
|
|
}
|
|
|
|
sub print_cpu_info
|
|
{
|
|
my $cpu = $cpu[0];
|
|
if ($kernel_arch =~ m/^ppc(64(le)?)?$/) {
|
|
print "# Processor: $cpu->{cpu} ($cpu->{revision})\n";
|
|
} elsif ($kernel_arch =~ m/^arm/) {
|
|
print "# Processor: $cpu->{'model name'}\n";
|
|
} elsif (exists $cpu->{'model name'} && exists $cpu->{'cpu family'}
|
|
&& exists $cpu->{'model'} && exists $cpu->{'stepping'}) {
|
|
print "# Processor: $cpu->{'model name'} ($cpu->{'cpu family'}/$cpu->{model}/$cpu->{stepping})\n";
|
|
} else {
|
|
print "# Cannot show processor info on $kernel_arch architecture.\n";
|
|
}
|
|
}
|
|
|
|
# @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: path,
|
|
# parent, name (directly taken from sysfs), driver and autoload.
|
|
use vars qw(@i2c_adapters);
|
|
|
|
# Find out whether the driver would be automatically loaded by the modalias
|
|
# mechanism.
|
|
sub device_driver_autoloads
|
|
{
|
|
my $device = shift;
|
|
|
|
my $modalias = sysfs_device_attribute($device, "modalias");
|
|
return 0 unless defined($modalias);
|
|
|
|
# At the moment we only handle PCI and USB drivers. Other driver
|
|
# types, most notably platform and i2c drivers, do not follow the
|
|
# device driver model to the letter, and often create their own
|
|
# devices. Such drivers appear to be autoloaded when they are not.
|
|
return 0 unless $modalias =~ m/^(pci|usb):/;
|
|
|
|
my $result = `modprobe -n -v --first-time $modalias 2>&1`;
|
|
return ($result =~ m/^insmod/ ||
|
|
$result =~ m/\balready in kernel\b/);
|
|
}
|
|
|
|
sub initialize_i2c_adapters_list
|
|
{
|
|
my ($entry, $base_dir, $have_i2c_adapter_class);
|
|
local $_;
|
|
|
|
if (-d "${sysfs_root}/class/i2c-adapter") {
|
|
$base_dir = "${sysfs_root}/class/i2c-adapter";
|
|
$have_i2c_adapter_class = 1;
|
|
} else {
|
|
$base_dir = "${sysfs_root}/bus/i2c/devices";
|
|
$have_i2c_adapter_class = 0;
|
|
}
|
|
opendir(local *ADAPTERS, $base_dir) or return;
|
|
|
|
while (defined($_ = readdir(ADAPTERS))) {
|
|
next unless m/^i2c-(\d+)$/;
|
|
my $nr = $1;
|
|
$entry = {}; # New entry
|
|
|
|
# The layout in sysfs has changed over the years
|
|
if ($have_i2c_adapter_class) {
|
|
my $link = readlink("${base_dir}/i2c-$nr/device");
|
|
if (!defined $link) {
|
|
$entry->{path} = "${base_dir}/i2c-$nr";
|
|
$entry->{parent} = "${base_dir}/i2c-$nr";
|
|
} elsif ($link =~ m/^(.*)\/i2c-$nr$/) {
|
|
$entry->{path} = "${base_dir}/i2c-$nr/device";
|
|
$entry->{parent} = "${base_dir}/i2c-$nr/$1";
|
|
}
|
|
}
|
|
|
|
# This works for all recent kernels (with or without
|
|
# CONFIG_I2C_COMPAT)
|
|
if (!defined $entry->{path}) {
|
|
my $link = readlink("${base_dir}/i2c-$nr");
|
|
$entry->{path} = "${base_dir}/i2c-$nr";
|
|
$link =~ s/(\/i2c-[0-9]+)+$//; # Handle multiplexers too
|
|
$entry->{parent} = "${base_dir}/$link";
|
|
}
|
|
|
|
$entry->{name} = sysfs_device_attribute($entry->{path}, "name");
|
|
next if $entry->{name} eq "ISA main adapter";
|
|
|
|
# First try to get the I2C adapter driver name from sysfs,
|
|
# and if it fails, fall back to searching our list of known
|
|
# I2C adapters.
|
|
$entry->{driver} = sysfs_device_module($entry->{parent})
|
|
|| find_i2c_adapter_driver($entry->{name})
|
|
|| 'UNKNOWN';
|
|
|
|
$entry->{autoload} = device_driver_autoloads($entry->{parent});
|
|
$i2c_adapters[$nr] = $entry;
|
|
}
|
|
closedir(ADAPTERS);
|
|
}
|
|
|
|
# %hwmon_autoloaded is a list of hwmon drivers which are autoloaded
|
|
# (typically by udev.) We don't need to load these drivers ourselves.
|
|
use vars qw(%hwmon_autoloaded);
|
|
|
|
sub initialize_hwmon_autoloaded
|
|
{
|
|
my $class_dir = "${sysfs_root}/class/hwmon";
|
|
opendir(local *HWMON, $class_dir) or return;
|
|
|
|
my ($hwmon, $driver);
|
|
while (defined($hwmon = readdir(HWMON))) {
|
|
next unless $hwmon =~ m/^hwmon\d+$/;
|
|
|
|
$driver = sysfs_device_module("${class_dir}/$hwmon/device");
|
|
next unless defined($driver);
|
|
|
|
if (device_driver_autoloads("${class_dir}/$hwmon/device")) {
|
|
$driver =~ tr/-/_/;
|
|
$hwmon_autoloaded{$driver}++
|
|
}
|
|
}
|
|
closedir(HWMON);
|
|
}
|
|
|
|
sub hwmon_is_autoloaded
|
|
{
|
|
my $driver = shift;
|
|
$driver =~ tr/-/_/;
|
|
return exists($hwmon_autoloaded{$driver});
|
|
}
|
|
|
|
###########
|
|
# MODULES #
|
|
###########
|
|
|
|
use vars qw(%modules_builtin %modules_list %modules_supported @modules_we_loaded);
|
|
|
|
sub initialize_modules_list
|
|
{
|
|
local ($_, *INPUTFILE);
|
|
|
|
# Starting with kernel 2.6.33, a list of built-in modules is available
|
|
if (open(*INPUTFILE, "/lib/modules/$kernel_version/modules.builtin")) {
|
|
while (<INPUTFILE>) {
|
|
tr/-/_/;
|
|
$modules_builtin{$1} = 1 if m/\/([^\/]+)\.ko$/;
|
|
}
|
|
close(INPUTFILE);
|
|
}
|
|
|
|
# List all loaded modules
|
|
open(*INPUTFILE, "/proc/modules") or return;
|
|
while (<INPUTFILE>) {
|
|
$modules_list{$1} = 1 if m/^(\S*)/;
|
|
}
|
|
}
|
|
|
|
sub is_module_loaded
|
|
{
|
|
my $module = shift;
|
|
$module =~ tr/-/_/;
|
|
return exists $modules_list{$module} || exists $modules_builtin{$module};
|
|
}
|
|
|
|
sub is_module_builtin
|
|
{
|
|
my $module = shift;
|
|
$module =~ tr/-/_/;
|
|
return exists $modules_builtin{$module};
|
|
}
|
|
|
|
sub load_module
|
|
{
|
|
my $module = shift;
|
|
|
|
return if is_module_loaded($module);
|
|
|
|
system("modprobe", $module);
|
|
if (($? >> 8) != 0) {
|
|
print "Failed to load module $module.\n";
|
|
return -1;
|
|
}
|
|
|
|
print "Module $module loaded successfully.\n";
|
|
push @modules_we_loaded, $module;
|
|
|
|
# Update the list of loaded modules
|
|
my $normalized = $module;
|
|
$normalized =~ tr/-/_/;
|
|
$modules_list{$normalized} = 1;
|
|
}
|
|
|
|
# udev may take some time to create device nodes when loading modules
|
|
sub udev_settle
|
|
{
|
|
if (!(-x "/sbin/udevadm" && system("/sbin/udevadm settle") == 0)
|
|
&& !(-x "/sbin/udevsettle" && system("/sbin/udevsettle") == 0)) {
|
|
sleep(1);
|
|
}
|
|
}
|
|
|
|
sub initialize_modules_supported
|
|
{
|
|
foreach my $chip (@chip_ids) {
|
|
next if $chip->{driver} eq "to-be-written";
|
|
next if $chip->{driver} eq "use-isa-instead";
|
|
|
|
my $normalized = $chip->{driver};
|
|
$normalized =~ tr/-/_/;
|
|
$modules_supported{$normalized}++;
|
|
}
|
|
}
|
|
|
|
sub unload_modules
|
|
{
|
|
return unless @modules_we_loaded;
|
|
|
|
# Attempt to unload all kernel drivers we loaded ourselves
|
|
while (my $module = pop @modules_we_loaded) {
|
|
print "Unloading $module... ";
|
|
system("modprobe -r $module 2> /dev/null");
|
|
if (($? >> 8) == 0) {
|
|
print "OK\n";
|
|
} else {
|
|
print "failed\n";
|
|
}
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
############
|
|
# DMI DATA #
|
|
############
|
|
|
|
use vars qw(%dmi $dmidecode_ok);
|
|
|
|
# Returns: 1 if dmidecode is recent enough, 0 if not
|
|
# Cache the result in case it is needed again later.
|
|
sub check_dmidecode_version
|
|
{
|
|
return $dmidecode_ok if defined $dmidecode_ok;
|
|
|
|
my $version;
|
|
if (open(local *DMIDECODE, "dmidecode --version 2>/dev/null |")) {
|
|
$version = <DMIDECODE>;
|
|
close(DMIDECODE);
|
|
chomp $version if defined $version;
|
|
}
|
|
|
|
if (!defined $version
|
|
|| !($version =~ m/^(\d+).(\d+)$/)
|
|
|| !(($1 == 2 && $2 >= 7) || $1 > 2)) {
|
|
print "# DMI data unavailable, please consider installing dmidecode 2.7\n".
|
|
"# or later for better results.\n";
|
|
$dmidecode_ok = 0;
|
|
} else {
|
|
$dmidecode_ok = 1;
|
|
}
|
|
|
|
return $dmidecode_ok;
|
|
}
|
|
|
|
sub initialize_dmi_data
|
|
{
|
|
my %items = (
|
|
# sysfs file name => dmidecode string name
|
|
sys_vendor => 'system-manufacturer',
|
|
product_name => 'system-product-name',
|
|
product_version => 'system-version',
|
|
board_vendor => 'baseboard-manufacturer',
|
|
board_name => 'baseboard-product-name',
|
|
board_version => 'baseboard-product-name',
|
|
chassis_type => 'chassis-type',
|
|
);
|
|
# Many BIOS have broken DMI data, filter it out
|
|
my %fake = (
|
|
'system manufacturer' => 1,
|
|
'system product name' => 1,
|
|
'system name' => 1,
|
|
'system version' => 1,
|
|
'to be filled by o.e.m.' => 1,
|
|
);
|
|
my $dmi_id_dir;
|
|
|
|
# First try reading the strings from sysfs if available
|
|
if (-d ($dmi_id_dir = "$sysfs_root/devices/virtual/dmi/id") # kernel >= 2.6.28
|
|
|| -d ($dmi_id_dir = "$sysfs_root/class/dmi/id")) {
|
|
foreach my $k (keys %items) {
|
|
$dmi{$k} = sysfs_device_attribute($dmi_id_dir, $k);
|
|
}
|
|
} else {
|
|
# Else fallback on calling dmidecode
|
|
return unless check_dmidecode_version();
|
|
|
|
foreach my $k (keys %items) {
|
|
next unless open(local *DMIDECODE,
|
|
"dmidecode -s $items{$k} 2>/dev/null |");
|
|
$dmi{$k} = <DMIDECODE>;
|
|
close(DMIDECODE);
|
|
}
|
|
}
|
|
|
|
# Strip trailing white-space, discard empty field
|
|
foreach my $k (keys %dmi) {
|
|
if (!defined $dmi{$k}) {
|
|
delete $dmi{$k};
|
|
next;
|
|
}
|
|
$dmi{$k} =~ s/\s*$//;
|
|
delete $dmi{$k} if $dmi{$k} eq '' || exists $fake{lc($dmi{$k})};
|
|
}
|
|
}
|
|
|
|
sub print_dmi_summary
|
|
{
|
|
my ($system, $board);
|
|
if (defined $dmi{sys_vendor} && defined $dmi{product_name}) {
|
|
$system = "$dmi{sys_vendor} $dmi{product_name}";
|
|
}
|
|
if (defined $dmi{board_vendor} && defined $dmi{board_name}) {
|
|
$board = "$dmi{board_vendor} $dmi{board_name}";
|
|
}
|
|
|
|
if (defined $system) {
|
|
print "# System: $system";
|
|
print " [$dmi{product_version}]" if defined $dmi{product_version};
|
|
print " (laptop)" if (is_laptop());
|
|
print "\n";
|
|
}
|
|
print "# Board: $board\n" if defined $board
|
|
&& (!defined $system || $system ne $board);
|
|
}
|
|
|
|
sub dmi_match
|
|
{
|
|
my $key = shift;
|
|
my $value;
|
|
|
|
return unless defined $dmi{$key};
|
|
while (defined ($value = shift)) {
|
|
return 1 if $dmi{$key} =~ m/\b$value\b/i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
sub is_laptop
|
|
{
|
|
return 0 unless $dmi{chassis_type};
|
|
return 1 if $dmi{chassis_type} =~ m/(Portable|Laptop|Notebook|Hand Held)/i;
|
|
return 1 if $dmi{chassis_type} =~ m/^(8|9|10|11|14)$/;
|
|
return 0;
|
|
}
|
|
|
|
#################
|
|
# SYSFS HELPERS #
|
|
#################
|
|
|
|
# From a sysfs device path, return the module name, or undef
|
|
sub sysfs_device_module
|
|
{
|
|
my $device = shift;
|
|
|
|
my $link = readlink("$device/driver/module");
|
|
return unless defined $link;
|
|
return basename($link);
|
|
}
|
|
|
|
# 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, return the subsystem name, or undef
|
|
sub sysfs_device_subsystem
|
|
{
|
|
my $device = shift;
|
|
|
|
my $link = readlink("$device/subsystem");
|
|
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;
|
|
$value = <FILE>;
|
|
close(FILE);
|
|
return unless defined $value;
|
|
|
|
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.
|
|
sub read_sys_dev_pci
|
|
{
|
|
my $devices = shift;
|
|
my ($dev, @pci_list);
|
|
|
|
opendir(local *DEVICES, "$devices")
|
|
or return \@pci_list;
|
|
|
|
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;
|
|
}
|
|
|
|
sub initialize_pci
|
|
{
|
|
my $pci_list;
|
|
local $_;
|
|
|
|
$pci_list = read_sys_dev_pci("$sysfs_root/bus/pci/devices");
|
|
|
|
# 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 #
|
|
#####################
|
|
|
|
# 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, %smbus, $count);
|
|
|
|
# Build a list of detected SMBus devices
|
|
foreach $key (keys %pci_list) {
|
|
$device = $pci_list{$key};
|
|
$smbus{$key}++
|
|
if exists $device->{class} &&
|
|
($device->{class} == 0x0c01 || # Access Bus
|
|
$device->{class} == 0x0c05); # SMBus
|
|
}
|
|
|
|
# Loop over the known I2C/SMBus adapters
|
|
foreach $try (@pci_adapters) {
|
|
$key = sprintf("%04x:%04x", $try->{vendid}, $try->{devid});
|
|
next unless exists $pci_list{$key};
|
|
|
|
$device = $pci_list{$key};
|
|
|
|
if ($try->{driver} eq "to-be-written") {
|
|
printf "No known driver for device \%s: \%s\n",
|
|
pci_busid($device), $try->{procid};
|
|
} else {
|
|
printf "Using driver `\%s' for device \%s: \%s\n",
|
|
$try->{driver}, pci_busid($device),
|
|
$try->{procid};
|
|
$count++;
|
|
load_module($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);
|
|
}
|
|
|
|
print "Sorry, no supported PCI bus adapters found.\n"
|
|
unless $count;
|
|
}
|
|
|
|
# $_[0]: Adapter description as found in sysfs
|
|
sub find_i2c_adapter_driver
|
|
{
|
|
my $name = shift;
|
|
my $entry;
|
|
|
|
foreach $entry (@i2c_adapter_names) {
|
|
return $entry->{driver}
|
|
if $name =~ $entry->{match};
|
|
}
|
|
}
|
|
|
|
#############################
|
|
# I2C AND SMBUS /DEV ACCESS #
|
|
#############################
|
|
|
|
# This should really go into a separate module/package.
|
|
|
|
# These are copied from <linux/i2c-dev.h>
|
|
|
|
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;
|
|
use constant I2C_FUNC_SMBUS_READ_BYTE_DATA => 0x00080000;
|
|
|
|
# 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) = @_;
|
|
|
|
# Reset register data cache
|
|
@i2c_byte_cache = ();
|
|
|
|
$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
|
|
# <linux/i2c-dev.h>). You should not need to call this directly.
|
|
# $_[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
|
|
# 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.
|
|
# Read byte data values are cached by default. As we keep reading the
|
|
# same registers over and over again in the detection functions, and
|
|
# SMBus can be slow, caching results in a big performance boost.
|
|
sub i2c_smbus_read_byte_data
|
|
{
|
|
my ($file, $command, $nocache) = @_;
|
|
my @data;
|
|
|
|
return $i2c_byte_cache[$command]
|
|
if !$nocache && exists $i2c_byte_cache[$command];
|
|
|
|
i2c_smbus_access($file, SMBUS_READ, $command, SMBUS_BYTE_DATA, \@data)
|
|
or return -1;
|
|
return ($i2c_byte_cache[$command] = $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 endianness.
|
|
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) = @_;
|
|
|
|
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, []);
|
|
} elsif ($addr == 0x73) {
|
|
# Special case for FSC chips, as at least the Syleus locks
|
|
# up with our regular probe code. Note that to our current
|
|
# knowledge only FSC chips live on this address, and for them
|
|
# this probe method is safe.
|
|
return 0 unless ($funcs & I2C_FUNC_SMBUS_READ_BYTE_DATA);
|
|
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, []);
|
|
}
|
|
}
|
|
|
|
# $_[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 hash
|
|
# where keys are driver names and values are detected chip information in
|
|
# the form of a list of hashes of type 'detect_data'.
|
|
|
|
# Type detect_data:
|
|
# A hash
|
|
# with field 'i2c_devnr', contianing the /dev/i2c-* number of 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
|
|
# with optional field 'alias_detect', containing a reference to an alias
|
|
# detection function for this chip
|
|
|
|
# This adds a detection to the above structure.
|
|
# 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, $detected_ref, $detected_entry, $driver,
|
|
$put_in_detected, @hash_addrs, @entry_addrs);
|
|
|
|
# Find out whether our new entry should go into the detected list
|
|
# or not. We compare all i2c addresses; if at least one matches,
|
|
# but our confidence value is lower, we assume this is a misdetection,
|
|
# in which case we simply discard our new entry.
|
|
@hash_addrs = ($datahash->{i2c_addr});
|
|
push @hash_addrs, @{$datahash->{i2c_sub_addrs}}
|
|
if exists $datahash->{i2c_sub_addrs};
|
|
$put_in_detected = 1;
|
|
FIND_LOOP:
|
|
foreach $detected_ref (values %chips_detected) {
|
|
foreach $detected_entry (@{$detected_ref}) {
|
|
next unless defined $detected_entry->{i2c_addr};
|
|
@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} &&
|
|
any_list_match(\@entry_addrs, \@hash_addrs)) {
|
|
if ($detected_entry->{conf} >= $datahash->{conf}) {
|
|
$put_in_detected = 0;
|
|
}
|
|
last FIND_LOOP;
|
|
}
|
|
}
|
|
}
|
|
|
|
return unless $put_in_detected;
|
|
|
|
# Here, we discard all entries 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.
|
|
foreach $driver (keys %chips_detected) {
|
|
$detected_ref = $chips_detected{$driver};
|
|
for ($i = @$detected_ref-1; $i >=0; $i--) {
|
|
next unless defined $detected_ref->[$i]->{i2c_addr};
|
|
@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} &&
|
|
any_list_match(\@entry_addrs, \@hash_addrs)) {
|
|
splice @$detected_ref, $i, 1;
|
|
delete $chips_detected{$driver}
|
|
if (!@$detected_ref);
|
|
}
|
|
}
|
|
}
|
|
|
|
# Now add the new entry to detected
|
|
$chips_detected{$chipdriver} = []
|
|
unless exists $chips_detected{$chipdriver};
|
|
push @{$chips_detected{$chipdriver}}, $datahash;
|
|
}
|
|
|
|
# Fake i2c drivers such as "not-a-sensor" or "use-isa-instead" have to be
|
|
# inserted so that confidence comparison can be performed. But then we have
|
|
# to filter them out so that the user doesn't get confused.
|
|
sub filter_out_fake_i2c_drivers
|
|
{
|
|
delete $chips_detected{"not-a-sensor"};
|
|
delete $chips_detected{"use-isa-instead"};
|
|
}
|
|
|
|
# This adds a detection to the above structure.
|
|
# $_[0]: chip driver
|
|
# $_[1]: reference to data hash
|
|
sub add_isa_to_chips_detected
|
|
{
|
|
my ($chipdriver, $datahash) = @_;
|
|
my ($i, $new_detected_ref, $detected_ref);
|
|
|
|
# First determine where the hash has to be added.
|
|
$chips_detected{$chipdriver} = []
|
|
unless exists $chips_detected{$chipdriver};
|
|
$new_detected_ref = $chips_detected{$chipdriver};
|
|
|
|
# Find out whether our new entry should go into the detected list
|
|
# or not. We only compare main isa_addr here, of course.
|
|
foreach $detected_ref (values %chips_detected) {
|
|
for ($i = 0; $i < @{$detected_ref}; $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}) {
|
|
splice @$detected_ref, $i, 1;
|
|
push @$new_detected_ref, $datahash;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Not found? OK, put it in the detected list
|
|
push @$new_detected_ref, $datahash;
|
|
}
|
|
|
|
# $_[0]: reference to an array of chips which may be aliases of each other
|
|
sub find_aliases
|
|
{
|
|
my $detected = shift;
|
|
my ($isa, $i2c, $dev, $alias_detect, $is_alias);
|
|
|
|
for ($isa = 0; $isa < @{$detected}; $isa++) {
|
|
# Look for chips with an ISA address but no I2C address
|
|
next unless defined $detected->[$isa];
|
|
next unless $detected->[$isa]->{isa_addr};
|
|
next if defined $detected->[$isa]->{i2c_addr};
|
|
# Additionally, the chip must possibly have I2C aliases
|
|
next unless defined $detected->[$isa]->{alias_detect};
|
|
|
|
for ($i2c = 0; $i2c < @{$detected}; $i2c++) {
|
|
# Look for chips with an I2C address but no ISA address
|
|
next unless defined $detected->[$i2c];
|
|
next unless defined $detected->[$i2c]->{i2c_addr};
|
|
next if $detected->[$i2c]->{isa_addr};
|
|
# Additionally, it must have the same chip name
|
|
next unless $detected->[$i2c]->{chipname} eq
|
|
$detected->[$isa]->{chipname};
|
|
|
|
# We have potential aliases, check if they actually are
|
|
$dev = $dev_i2c.$detected->[$i2c]->{i2c_devnr};
|
|
open(local *FILE, $dev) or
|
|
print("Can't open $dev\n"),
|
|
next;
|
|
binmode(FILE);
|
|
i2c_set_slave_addr(\*FILE, $detected->[$i2c]->{i2c_addr}) or
|
|
print("Can't set I2C address for $dev\n"),
|
|
next;
|
|
|
|
if (initialize_ioports()) {
|
|
$alias_detect = $detected->[$isa]->{alias_detect};
|
|
$is_alias = &$alias_detect($detected->[$isa]->{isa_addr},
|
|
\*FILE,
|
|
$detected->[$i2c]->{i2c_addr});
|
|
close_ioports();
|
|
}
|
|
close(FILE);
|
|
|
|
next unless $is_alias;
|
|
# This is an alias: copy the I2C data into the ISA
|
|
# entry, then discard the I2C entry
|
|
$detected->[$isa]->{i2c_devnr} = $detected->[$i2c]->{i2c_devnr};
|
|
$detected->[$isa]->{i2c_addr} = $detected->[$i2c]->{i2c_addr};
|
|
$detected->[$isa]->{i2c_sub_addr} = $detected->[$i2c]->{i2c_sub_addr};
|
|
undef $detected->[$i2c];
|
|
last;
|
|
}
|
|
}
|
|
|
|
# The loops above may have made the chip list sparse, make it
|
|
# compact again
|
|
for ($isa = 0; $isa < @{$detected}; ) {
|
|
if (defined($detected->[$isa])) {
|
|
$isa++;
|
|
} else {
|
|
splice @{$detected}, $isa, 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
# 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};
|
|
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]: Address
|
|
sub add_busy_i2c_address
|
|
{
|
|
my ($adapter_nr, $addr) = @_;
|
|
# If the address is busy, we can normally find out which driver
|
|
# requested it (if the kernel is recent enough, at least 2.6.16 and
|
|
# later are known to work), and we assume it is the right one.
|
|
my ($device, $driver, $module, $new_hash);
|
|
|
|
$device = sprintf("$sysfs_root/bus/i2c/devices/\%d-\%04x",
|
|
$adapter_nr, $addr);
|
|
$driver = sysfs_device_driver($device);
|
|
$module = sysfs_device_module($device);
|
|
|
|
if (!defined($driver)) {
|
|
printf("Client at address 0x%02x can not be probed - ".
|
|
"unload all client drivers first!\n", $addr);
|
|
return;
|
|
}
|
|
|
|
$new_hash = {
|
|
conf => 6, # Arbitrary confidence
|
|
i2c_addr => $addr,
|
|
chipname => sysfs_device_attribute($device, "name")
|
|
|| "unknown",
|
|
i2c_devnr => $adapter_nr,
|
|
};
|
|
|
|
printf "Client found at address 0x\%02x\n", $addr;
|
|
if (defined($module)) {
|
|
printf "Handled by driver `\%s' (already loaded), chip type `\%s'\n",
|
|
$module, $new_hash->{chipname};
|
|
} else {
|
|
printf "Handled by driver `\%s' (built-in), chip type `\%s'\n",
|
|
$driver, $new_hash->{chipname};
|
|
# Let's hope that the driver name matches the module name
|
|
$module = $driver;
|
|
}
|
|
|
|
# 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{$module}) {
|
|
add_i2c_to_chips_detected($module, $new_hash);
|
|
} else {
|
|
print " (note: this is probably NOT a sensor chip!)\n";
|
|
}
|
|
}
|
|
|
|
# $_[0]: The number of the adapter to scan
|
|
# $_[1]: Address
|
|
# $_[2]: Chip being probed
|
|
sub probe_free_i2c_address
|
|
{
|
|
my ($adapter_nr, $addr, $chip) = @_;
|
|
my ($conf, @other_addr, $new_hash);
|
|
|
|
printf("\%-60s", sprintf("Probing for `\%s'... ", $chip->{name}));
|
|
if (($conf, @other_addr) = &{$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 (@other_addr) {
|
|
print ", other addresses:";
|
|
@other_addr = sort @other_addr;
|
|
foreach my $other_addr (@other_addr) {
|
|
printf(" 0x%02x", $other_addr);
|
|
}
|
|
}
|
|
print ")\n";
|
|
|
|
$new_hash = {
|
|
conf => $conf,
|
|
i2c_addr => $addr,
|
|
chipname => $chip->{name},
|
|
i2c_devnr => $adapter_nr,
|
|
};
|
|
if (@other_addr) {
|
|
my @other_addr_copy = @other_addr;
|
|
$new_hash->{i2c_sub_addrs} = \@other_addr_copy;
|
|
}
|
|
add_i2c_to_chips_detected($chip->{driver}, $new_hash);
|
|
} else {
|
|
print "No\n";
|
|
}
|
|
}
|
|
|
|
# $_[0]: The device to check (PCI or not)
|
|
# Returns: PCI class of the adapter if available, 0 if not
|
|
sub get_pci_class
|
|
{
|
|
my ($device) = @_;
|
|
my ($subsystem, $class);
|
|
|
|
$subsystem = sysfs_device_subsystem($device);
|
|
return 0 unless defined $subsystem;
|
|
return 0x0300 if $subsystem eq "drm"; # Graphics card
|
|
return 0 unless $subsystem eq "pci";
|
|
|
|
$class = sysfs_device_attribute($device, "class");
|
|
return 0 unless defined $class;
|
|
$class = oct($class) if $class =~ /^0/;
|
|
return $class >> 8;
|
|
}
|
|
|
|
# $_[0]: The number of the adapter to scan
|
|
sub scan_i2c_adapter
|
|
{
|
|
my ($adapter_nr, $smbus_default) = @_;
|
|
my ($funcs, $chip, $addr, $class, $default, $input, @not_to_scan);
|
|
|
|
$class = get_pci_class($i2c_adapters[$adapter_nr]->{parent});
|
|
# Do not probe adapters on multimedia and graphics cards by default
|
|
if (($class & 0xff00) == 0x0400 || ($class & 0xff00) == 0x0300) {
|
|
$default = 0;
|
|
} elsif ($class == 0x0c01 || $class == 0x0c05
|
|
|| find_i2c_adapter_driver($i2c_adapters[$adapter_nr]->{name})) {
|
|
$default = $smbus_default;
|
|
} else {
|
|
$default = 1;
|
|
}
|
|
|
|
printf "Next adapter: $i2c_adapters[$adapter_nr]->{name} (i2c-$adapter_nr)\n".
|
|
"Do you want to scan it? (\%s/selectively): ",
|
|
$default ? "YES/no" : "yes/NO";
|
|
|
|
$input = read_answer();
|
|
if ($input =~ /^\s*n/i
|
|
|| (!$default && $input !~ /^\s*[ys]/i)) {
|
|
print "\n";
|
|
return;
|
|
}
|
|
|
|
if ($input =~ /^\s*s/i) {
|
|
print "Please enter one or more addresses not to scan. Separate them with commas.\n",
|
|
"You can specify a range by using dashes. Example: 0x58-0x5f,0x69.\n",
|
|
"Addresses: ";
|
|
$input = read_answer();
|
|
chomp($input);
|
|
@not_to_scan = parse_not_to_scan(0x03, 0x77, $input);
|
|
} elsif (($class & 0xff00) == 0x0300) {
|
|
# Skip EDID and DDC/CI addresses by default on graphics
|
|
# adapters. Also skip address 0x4f which was reported in a
|
|
# display corruption case.
|
|
@not_to_scan = parse_not_to_scan(0x03, 0x77, "0x37, 0x50-0x57, 0x4f");
|
|
}
|
|
|
|
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 | I2C_FUNC_SMBUS_READ_BYTE_DATA))) {
|
|
print "Adapter cannot be probed, skipping.\n";
|
|
return;
|
|
}
|
|
if (~$funcs & (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
|
|
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)) {
|
|
add_busy_i2c_address($adapter_nr, $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, @non_hwmon_chip_ids) {
|
|
next unless exists $chip->{i2c_addrs}
|
|
&& contains($addr, @{$chip->{i2c_addrs}});
|
|
probe_free_i2c_address($adapter_nr, $addr, $chip);
|
|
}
|
|
$| = 0;
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
sub scan_isa_bus
|
|
{
|
|
my $chip_list_ref = shift;
|
|
my ($chip, $addr, $conf);
|
|
|
|
$| = 1;
|
|
foreach $chip (@{$chip_list_ref}) {
|
|
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},
|
|
alias_detect => $chip->{alias_detect},
|
|
};
|
|
add_isa_to_chips_detected($chip->{driver}, $new_hash);
|
|
}
|
|
}
|
|
$| = 0;
|
|
}
|
|
|
|
use vars qw(%standard_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 registers within the logical device
|
|
%standard_superio = (
|
|
devidreg => 0x20,
|
|
logdevreg => 0x07,
|
|
actreg => 0x30,
|
|
actmask => 0x01,
|
|
basereg_msb => 0x60,
|
|
basereg_lsb => 0x61,
|
|
);
|
|
|
|
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, $standard_superio{logdevreg});
|
|
$oldldn = inb($datareg);
|
|
|
|
for ($ldn = 0; $ldn < 16; $ldn++) {
|
|
# Select logical device
|
|
outb($addrreg, $standard_superio{logdevreg});
|
|
outb($datareg, $ldn);
|
|
|
|
# Read base I/O address
|
|
outb($addrreg, $standard_superio{basereg_msb});
|
|
$addr = inb($datareg) << 8;
|
|
outb($addrreg, $standard_superio{basereg_lsb});
|
|
$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, $standard_superio{logdevreg});
|
|
outb($datareg, $oldldn);
|
|
}
|
|
|
|
# Returns: features bitmask if device added to chips_detected, 0 if not
|
|
sub probe_superio
|
|
{
|
|
my ($addrreg, $datareg, $chip) = @_;
|
|
my ($val, $addr);
|
|
my %superio;
|
|
|
|
# Use chip-specific registers if provided
|
|
if (exists $chip->{regs}) {
|
|
%superio = overlay_hash(\%standard_superio, $chip->{regs});
|
|
} else {
|
|
%superio = %standard_superio;
|
|
}
|
|
|
|
if (exists $chip->{check}) {
|
|
return 0 unless $chip->{check}($addrreg, $datareg);
|
|
}
|
|
|
|
printf "\%-60s", "Found `$chip->{name}'";
|
|
|
|
# Does it have hardware monitoring capabilities?
|
|
if (!exists $chip->{driver}) {
|
|
print "\n (no information available)\n";
|
|
return 0;
|
|
}
|
|
if ($chip->{driver} eq "not-a-sensor") {
|
|
print "\n (no hardware monitoring capabilities)\n";
|
|
return 0;
|
|
}
|
|
if ($chip->{driver} eq "via-smbus-only") {
|
|
print "\n (hardware monitoring capabilities accessible via SMBus only)\n";
|
|
return FEAT_SMBUS;
|
|
}
|
|
if (!exists $chip->{logdev}) {
|
|
print "\n (no support yet)\n";
|
|
return 0;
|
|
}
|
|
|
|
# Switch to the sensor logical device
|
|
outb($addrreg, $superio{logdevreg});
|
|
outb($datareg, $chip->{logdev});
|
|
|
|
# Get the IO base address
|
|
outb($addrreg, $superio{basereg_msb});
|
|
$addr = inb($datareg);
|
|
outb($addrreg, $superio{basereg_lsb});
|
|
$addr = ($addr << 8) | inb($datareg);
|
|
|
|
# Check the activation register and base address
|
|
outb($addrreg, $superio{actreg});
|
|
$val = inb($datareg);
|
|
if (!($val & $superio{actmask})) {
|
|
if ($addr && $addr != 0xffff) {
|
|
printf "\n (address 0x\%x, but not activated)\n", $addr;
|
|
} else {
|
|
print "\n (but not activated)\n";
|
|
}
|
|
return 0;
|
|
}
|
|
if ($addr == 0 || $addr == 0xffff) {
|
|
print "\n (but no address specified)\n";
|
|
return 0;
|
|
}
|
|
|
|
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->{driver}, $new_hash);
|
|
return $chip->{features};
|
|
}
|
|
|
|
# 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);
|
|
return 0 if $val == 0x00 || $val == 0xff;
|
|
|
|
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;
|
|
}
|
|
|
|
# Returns: features supported by the device added, if any
|
|
sub scan_superio
|
|
{
|
|
my ($addrreg, $datareg) = @_;
|
|
my ($val, $found);
|
|
my $features = 0;
|
|
|
|
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);
|
|
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})) {
|
|
last;
|
|
}
|
|
|
|
# did it work?
|
|
outb($addrreg, $standard_superio{devidreg});
|
|
$val = inb($datareg);
|
|
outb($addrreg, $standard_superio{devidreg} + 1);
|
|
$val = ($val << 8) | inb($datareg);
|
|
if ($val == 0x0000 || $val == 0xffff) {
|
|
print "No\n";
|
|
next;
|
|
}
|
|
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})) {
|
|
$features |= 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};
|
|
}
|
|
last;
|
|
}
|
|
exit_superio($addrreg, $datareg);
|
|
$| = 0;
|
|
return $features;
|
|
}
|
|
|
|
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($entry->{driver}, $new_hash);
|
|
} else {
|
|
print "No\n";
|
|
}
|
|
}
|
|
|
|
##################
|
|
# CHIP DETECTION #
|
|
##################
|
|
|
|
# This routine allows you to dynamically update the chip detection list.
|
|
# The most common use is to allow for different chip to driver mappings
|
|
# based on different linux kernels
|
|
sub chip_special_cases
|
|
{
|
|
# Some chip to driver mappings depend on the environment
|
|
foreach my $chip (@chip_ids, @superio_ids_natsemi, @superio_ids_smsc,
|
|
@superio_ids_smsc_ns, @superio_ids_winbond,
|
|
@superio_ids_ite) {
|
|
if (ref($chip->{driver}) eq 'CODE') {
|
|
$chip->{driver} = $chip->{driver}->();
|
|
}
|
|
}
|
|
|
|
# Also fill the fake driver name of non-hwmon chips
|
|
foreach my $chip (@non_hwmon_chip_ids) {
|
|
$chip->{driver} = "not-a-sensor";
|
|
}
|
|
}
|
|
|
|
# Each function returns a confidence value. The higher this value, the more
|
|
# sure we are about this chip. This may help overrule false positives,
|
|
# although we also attempt to prevent false positives in the first place.
|
|
|
|
# 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.
|
|
|
|
# All I2C detection functions below take at least 2 parameters:
|
|
# $_[0]: Reference to the file descriptor to access the chip
|
|
# $_[1]: Address
|
|
# Some of these functions which can detect more than one type of device,
|
|
# take a third parameter:
|
|
# $_[2]: Chip to detect
|
|
|
|
# Registers used: 0x58
|
|
sub mtp008_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
return if i2c_smbus_read_byte_data($file, 0x58) != 0xac;
|
|
return 3;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM78, 2 = LM79
|
|
# Registers used:
|
|
# 0x40: Configuration
|
|
# 0x48: Full I2C Address
|
|
# 0x49: Device ID
|
|
sub lm78_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $reg;
|
|
|
|
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 if $chip == 0 && ($reg != 0x00 && $reg != 0x20 && $reg != 0x40);
|
|
return if $chip == 2 && ($reg & 0xfe) != 0xc0;
|
|
|
|
# Explicitly prevent misdetection of Winbond chips
|
|
$reg = i2c_smbus_read_byte_data($file, 0x4f);
|
|
return if $reg == 0xa3 || $reg == 0x5c;
|
|
|
|
return 6;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM75, 1 = DS75, 2 = LM75A
|
|
# Registers used:
|
|
# 0x00: Temperature
|
|
# 0x01: Configuration
|
|
# 0x02: Hysteresis
|
|
# 0x03: Overtemperature Shutdown
|
|
# 0x04-0x07: No registers
|
|
# 0x07: Device ID (LM75A only)
|
|
# 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.
|
|
# And the LM75A is again different, it cycles over 8-byte boundaries, but
|
|
# registers 0x04-0x06 return 0xff. Thankfully it has a device ID register
|
|
# at 0x07.
|
|
# 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 ($file, $addr, $chip) = @_;
|
|
my $i;
|
|
my $cur = i2c_smbus_read_byte_data($file, 0x00);
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x01);
|
|
my ($maxreg, $hyst, $os, $dev_id);
|
|
|
|
if ($chip == 2) { # LM75A
|
|
$dev_id = i2c_smbus_read_byte_data($file, 0x07);
|
|
return if $dev_id != 0xA1;
|
|
|
|
$hyst = i2c_smbus_read_byte_data($file, 0x02);
|
|
$os = i2c_smbus_read_byte_data($file, 0x03);
|
|
|
|
for $i (0x04 .. 0x06) {
|
|
return if i2c_smbus_read_byte_data($file, $i) != 0xff;
|
|
}
|
|
} else { # LM75 or DS75
|
|
$maxreg = $chip == 1 ? 0x0f : 0x07;
|
|
$hyst = i2c_smbus_read_byte_data($file, 0x02, NO_CACHE);
|
|
for $i (0x04 .. $maxreg) {
|
|
return if i2c_smbus_read_byte_data($file, $i, NO_CACHE) != $hyst;
|
|
}
|
|
|
|
$os = i2c_smbus_read_byte_data($file, 0x03, NO_CACHE);
|
|
for $i (0x04 .. $maxreg) {
|
|
return if i2c_smbus_read_byte_data($file, $i, NO_CACHE) != $os;
|
|
}
|
|
}
|
|
|
|
if ($chip != 1) { # LM75 or LM75A
|
|
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;
|
|
return if $chip == 2
|
|
and i2c_smbus_read_byte_data($file, $i + 0x07) != $dev_id;
|
|
}
|
|
}
|
|
|
|
# All registers hold the same value, obviously a misdetection
|
|
return if $conf == $cur and $cur == $hyst and $cur == $os;
|
|
|
|
# Unused bits
|
|
return if $chip != 1 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;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x00: Temperature
|
|
# 0x01: Configuration
|
|
# 0x02: High Limit
|
|
# 0x03: Low Limit
|
|
# 0x04: Status
|
|
# 0x07: Manufacturer ID and Product ID
|
|
sub lm73_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x01);
|
|
my $status = i2c_smbus_read_byte_data($file, 0x04);
|
|
|
|
# Bits that always return 0
|
|
return if ($conf & 0x0c) or ($status & 0x10);
|
|
|
|
# Test with byte read first to avoid confusing other chips
|
|
return if i2c_smbus_read_byte_data($file, 0x07) != 0x01
|
|
or i2c_smbus_read_word_data($file, 0x07) != 0x9001;
|
|
|
|
# Make sure the chip supports SMBus read word transactions
|
|
my $cur = i2c_smbus_read_word_data($file, 0x00);
|
|
return if $cur < 0;
|
|
my $high = i2c_smbus_read_word_data($file, 0x02);
|
|
return if $high < 0;
|
|
my $low = i2c_smbus_read_word_data($file, 0x03);
|
|
return if $low < 0;
|
|
return if ($cur & 0x0300) or (($high | $low) & 0x1f00);
|
|
|
|
return 3;
|
|
}
|
|
|
|
# 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 ($file, $addr) = @_;
|
|
my $i;
|
|
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, NO_CACHE);
|
|
return if i2c_smbus_read_byte_data($file, 0x06, NO_CACHE) != $low;
|
|
return if i2c_smbus_read_byte_data($file, 0x07, NO_CACHE) != $low;
|
|
|
|
my $high = i2c_smbus_read_byte_data($file, 0x05, NO_CACHE);
|
|
return if i2c_smbus_read_byte_data($file, 0x06, NO_CACHE) != $high;
|
|
return if i2c_smbus_read_byte_data($file, 0x07, NO_CACHE) != $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
|
|
foreach $i (0x00, 0x02, 0x03, 0x04, 0x05) {
|
|
return if i2c_smbus_read_word_data($file, $i) < 0;
|
|
}
|
|
|
|
return 3;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM92, 1 = LM76, 2 = MAX6633/MAX6634/MAX6635
|
|
# 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 ($file, $addr, $chip) = @_;
|
|
|
|
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 ($chip == 0) ? 4 : 2;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM80, 1 = LM96080
|
|
# Registers used:
|
|
# 0x00: Configuration register
|
|
# 0x02: Interrupt state register
|
|
# 0x07: Conversion rate register (LM96080 only)
|
|
# 0x2a-0x3d: Limits registers (LM80 only)
|
|
# 0x3e: Manufacturer's ID register (LM96080 only)
|
|
# 0x3f: Stepping/die revision ID register (LM96080 only)
|
|
# The LM80 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 ($file, $addr, $chip) = @_;
|
|
my ($i, $reg);
|
|
|
|
return if (i2c_smbus_read_byte_data($file, 0x00) & 0x80) != 0;
|
|
return if (i2c_smbus_read_byte_data($file, 0x02) & 0xc0) != 0;
|
|
|
|
if ($chip == 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 whether limits are in the correct order
|
|
# (min<max for voltages, hyst<max for temperature). Since it is still
|
|
# possible that the chip is an LM80 with limits not properly set,
|
|
# a few "errors" are tolerated.
|
|
my $confidence = 0;
|
|
for ($i = 0x2a; $i <= 0x3a; $i++) {
|
|
$confidence++
|
|
if i2c_smbus_read_byte_data($file, $i) < i2c_smbus_read_byte_data($file, $i+1);
|
|
}
|
|
# hot temp<OS temp
|
|
$confidence++
|
|
if i2c_smbus_read_byte_data($file, 0x38) < i2c_smbus_read_byte_data($file, 0x3a);
|
|
|
|
# Negative temperature limits are unlikely.
|
|
for ($i = 0x3a; $i <= 0x3d; $i++) {
|
|
$confidence++ if (i2c_smbus_read_byte_data($file, $i) & 0x80) == 0;
|
|
}
|
|
|
|
# $confidence is between 0 and 14
|
|
$confidence = ($confidence >> 1) - 4;
|
|
# $confidence is now between -4 and 3
|
|
|
|
return unless $confidence > 0;
|
|
return $confidence;
|
|
} elsif ($chip == 1) {
|
|
return if (i2c_smbus_read_byte_data($file, 0x07) & 0xfe) != 0;
|
|
return if i2c_smbus_read_byte_data($file, 0x3e) != 0x01;
|
|
return if i2c_smbus_read_byte_data($file, 0x3f) != 0x08;
|
|
|
|
return 6;
|
|
}
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x00: Configuration register
|
|
# 0x07: Conversion rate register
|
|
# 0x09: Oneshot register
|
|
# 0x0a: Shutdown register
|
|
# 0x0b: Advanced Configuration register
|
|
# 0x0c: Busy Status register
|
|
# 0x3e: Manufacturer's ID register
|
|
# 0x3f: Stepping/die revision ID register
|
|
sub adc128d818_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
|
|
return if i2c_smbus_read_byte_data($file, 0x3e) != 0x01;
|
|
return if i2c_smbus_read_byte_data($file, 0x3f) != 0x09;
|
|
|
|
return if (i2c_smbus_read_byte_data($file, 0x00) & 0xf4) != 0;
|
|
return if (i2c_smbus_read_byte_data($file, 0x07) & 0xfe) != 0;
|
|
return if (i2c_smbus_read_byte_data($file, 0x09) & 0xfe) != 0;
|
|
return if (i2c_smbus_read_byte_data($file, 0x0a) & 0xfe) != 0;
|
|
return if (i2c_smbus_read_byte_data($file, 0x0b) & 0xf8) != 0;
|
|
return if (i2c_smbus_read_byte_data($file, 0x0c) & 0xfc) != 0;
|
|
|
|
return 7;
|
|
}
|
|
|
|
# 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 ($file, $addr) = @_;
|
|
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;
|
|
}
|
|
|
|
# Chip to detect: 0 = MAX6680/81, 1 = MAX6695/96
|
|
# Registers used:
|
|
# 0x03: Configuration
|
|
# 0x04: Conversion rate
|
|
# 0x12: Status2
|
|
# 0x16: Overtemperature 2
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: Chip ID / die revision
|
|
sub max6680_95_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $cid = i2c_smbus_read_byte_data($file, 0xff);
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x03);
|
|
my $mid = i2c_smbus_read_byte_data($file, 0xfe, NO_CACHE);
|
|
my $emerg = i2c_smbus_read_byte_data($file, 0x16, NO_CACHE);
|
|
my $rate = i2c_smbus_read_byte_data($file, 0x04, NO_CACHE);
|
|
my $emerg2 = i2c_smbus_read_byte_data($file, 0x16, NO_CACHE);
|
|
|
|
# Check common conditions
|
|
return if $rate > 0x07;
|
|
return if $mid != 0x4d; # Not Maxim
|
|
return if $cid != 0x01; # None of the chips we are looking for
|
|
|
|
if ($chip == 0) {
|
|
return if ($conf & 0x03) != 0;
|
|
return 8 if $emerg != $emerg2; # MAX6680/MAX6681
|
|
}
|
|
if ($chip == 1) {
|
|
my $status2 = i2c_smbus_read_byte_data($file, 0x12);
|
|
|
|
return if ($conf & 0x10) != 0;
|
|
return if ($status2 & 0x01) != 0;
|
|
return 8 if $emerg == $emerg2; # MAX6695/MAX6696
|
|
}
|
|
return;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM90, 1 = LM89/LM99, 2 = LM86, 3 = ADM1032,
|
|
# 4 = MAX6654, 5 = ADT7461,
|
|
# 6 = MAX6646/MAX6647/MAX6648/MAX6649/MAX6692,
|
|
# 8 = W83L771W/G,
|
|
# 11 = W83L771AWG/ASG, 12 = MAX6690,
|
|
# 13 = ADT7461A/NCT1008, 14 = SA56004,
|
|
# 15 = G781
|
|
# Registers used:
|
|
# 0x03: Configuration
|
|
# 0x04: Conversion rate
|
|
# 0xbf: Configuration 2 (National Semiconductor, Winbond, and Philips only)
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: Chip ID / die revision
|
|
sub lm90_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
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);
|
|
my $conf2 = i2c_smbus_read_byte_data($file, 0xbf);
|
|
|
|
if ($chip == 0) {
|
|
return if ($conf & 0x2a) != 0;
|
|
return if ($conf2 & 0xf8) != 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 ($conf2 & 0xf8) != 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 ($conf2 & 0xf8) != 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 6 if ($cid & 0xf0) == 0x40; # ADM1032
|
|
}
|
|
if ($chip == 4) {
|
|
return if ($conf & 0x07) != 0;
|
|
return if $rate > 0x07;
|
|
return if $mid != 0x4d; # Maxim
|
|
return 8 if $cid == 0x08; # MAX6654
|
|
}
|
|
if ($chip == 5) {
|
|
return if ($conf & 0x1b) != 0;
|
|
return if $rate > 0x0a;
|
|
return if $mid != 0x41; # Analog Devices
|
|
return 8 if $cid == 0x51; # ADT7461
|
|
}
|
|
if ($chip == 6) {
|
|
return if ($conf & 0x3f) != 0;
|
|
return if $rate > 0x07;
|
|
return if $mid != 0x4d; # Maxim
|
|
return 8 if $cid == 0x59; # MAX6648/MAX6692
|
|
}
|
|
if ($chip == 8) {
|
|
return if ($conf & 0x2a) != 0;
|
|
return if ($conf2 & 0xf8) != 0;
|
|
return if $rate > 0x09;
|
|
return if $mid != 0x5c; # Winbond
|
|
return 6 if ($cid & 0xfe) == 0x00; # W83L771W/G
|
|
}
|
|
if ($chip == 11) {
|
|
return if ($conf & 0x2a) != 0;
|
|
return if ($conf2 & 0xf8) != 0;
|
|
return if $rate > 0x08;
|
|
return if $mid != 0x5c; # Winbond
|
|
return 6 if ($cid & 0xfe) == 0x10; # W83L771AWG/ASG
|
|
}
|
|
if ($chip == 12) {
|
|
return if ($conf & 0x07) != 0;
|
|
return if $rate > 0x07;
|
|
return if $mid != 0x4d; # Maxim
|
|
return 8 if $cid == 0x09; # MAX6690
|
|
}
|
|
if ($chip == 13) {
|
|
return if ($conf & 0x1b) != 0;
|
|
return if $rate > 0x0a;
|
|
return if $mid != 0x41; # Analog Devices
|
|
return 8 if $cid == 0x57; # ADT7461A, NCT1008
|
|
}
|
|
if ($chip == 14) {
|
|
return if ($conf & 0x2a) != 0;
|
|
return if ($conf2 & 0xfe) != 0;
|
|
return if $rate > 0x09;
|
|
return if $mid != 0xa1; # NXP Semiconductor/Philips
|
|
return 6 if $cid == 0x00; # SA56004
|
|
}
|
|
if ($chip == 15) {
|
|
return if ($conf & 0x3f) != 0;
|
|
return if $rate > 0x08;
|
|
return if $mid != 0x47; # GMT
|
|
return 8 if $cid == 0x01; # G781
|
|
}
|
|
return;
|
|
}
|
|
|
|
# Chip to detect: 0 = TMP400, 1 = TMP401, 2 = TMP411A, 3 = TMP411B, 4 = TMP411C,
|
|
# 5 = TMP431, 6 = TMP432, 7 = TMP435, 8 = TMP451
|
|
# Registers used:
|
|
# 0x03: Configuration (4 unused bits)
|
|
# 0x04: Conversion rate (value 0x0f or lower, 0x09 or lower for TMP451)
|
|
# 0x10: Remote temperature low byte (4 unused bits)
|
|
# 0x13: Remote temperature low limit, low byte (4 unused bits)
|
|
# 0x14: Remote temperature high limit, low byte (4 unused bits)
|
|
# 0x22: Consecutive alert, 4 unused bits (TMP451 only)
|
|
# 0x24: Digital filter control, 6 unused bits (TMP451 only)
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: Device ID
|
|
sub tmp401_detect()
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
|
|
my $mid = i2c_smbus_read_byte_data($file, 0xfe);
|
|
return if ($mid != 0x55);
|
|
|
|
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);
|
|
my $rtemp = i2c_smbus_read_byte_data($file, 0x10);
|
|
my $rhigh = i2c_smbus_read_byte_data($file, 0x13);
|
|
my $rlow = i2c_smbus_read_byte_data($file, 0x14);
|
|
|
|
return if ($conf & 0x1b);
|
|
return if ($rate > 0x0f);
|
|
return if ($rtemp & 0x0f);
|
|
return if ($rhigh & 0x0f);
|
|
return if ($rlow & 0x0f);
|
|
|
|
return 8 if ($chip == 0 && $cid == 0x01); # TMP400
|
|
return 8 if ($chip == 1 && $cid == 0x11); # TMP401
|
|
return 8 if ($chip == 2 && $cid == 0x12); # TMP411A
|
|
return 8 if ($chip == 3 && $cid == 0x13); # TMP411B
|
|
return 8 if ($chip == 4 && $cid == 0x10); # TMP411C
|
|
return 8 if ($chip == 5 && $cid == 0x31); # TMP431
|
|
return 8 if ($chip == 6 && $cid == 0x32); # TMP432
|
|
return 8 if ($chip == 7 && $cid == 0x35); # TMP435
|
|
|
|
if ($chip == 8) { # TMP451
|
|
return if $cid != 0x00;
|
|
return if $rate > 0x09;
|
|
|
|
my $alert = i2c_smbus_read_byte_data($file, 0x22);
|
|
return if ($alert & 0x71) != 0x01;
|
|
|
|
my $filter = i2c_smbus_read_byte_data($file, 0x24);
|
|
return if $filter & 0xfc;
|
|
|
|
return 4;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
# Chip to detect: 0 = TMP421, 1 = TMP422, 2 = TMP423, 3 = TMP441, 4 = TMP442
|
|
# Registers used:
|
|
# 0x08: Status (7 unused bits)
|
|
# 0x0b: Conversion rate (value 0x07 or lower)
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: Device ID
|
|
sub tmp42x_detect()
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
|
|
my $mid = i2c_smbus_read_byte_data($file, 0xfe);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0xff);
|
|
my $status = i2c_smbus_read_byte_data($file, 0x08);
|
|
my $rate = i2c_smbus_read_byte_data($file, 0x0b);
|
|
|
|
return if ($mid != 0x55);
|
|
return if ($status & 0x7f);
|
|
return if ($rate > 0x07);
|
|
|
|
return 6 if ($chip == 0 && $cid == 0x21); # TMP421
|
|
return 6 if ($chip == 1 && $cid == 0x22); # TMP422
|
|
return 6 if ($chip == 2 && $cid == 0x23); # TMP423
|
|
return 6 if ($chip == 3 && $cid == 0x41); # TMP441
|
|
return 6 if ($chip == 4 && $cid == 0x42); # TMP442
|
|
|
|
return;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x3d: Device ID
|
|
# 0x3e: Company ID
|
|
sub amc6821_detect()
|
|
{
|
|
my ($file, $addr) = @_;
|
|
|
|
my $dev_id = i2c_smbus_read_byte_data($file, 0x3d);
|
|
my $comp_id = i2c_smbus_read_byte_data($file, 0x3e);
|
|
|
|
return if ($comp_id != 0x49); # Texas Instruments
|
|
|
|
# Bit 7 of register address is ignored
|
|
return if i2c_smbus_read_byte_data($file, 0x80 | 0x3d) != $dev_id;
|
|
return if i2c_smbus_read_byte_data($file, 0x80 | 0x3e) != $comp_id;
|
|
|
|
return 6 if ($dev_id == 0x21); # AMC6821
|
|
|
|
return;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x03: Configuration (no low nibble, returns the previous low nibble)
|
|
# 0x04: Conversion rate
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: no register
|
|
sub max6657_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0xfe, NO_CACHE);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0xff, NO_CACHE);
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x03, NO_CACHE);
|
|
|
|
return if $mid != 0x4d; # Maxim
|
|
return if ($conf & 0x1f) != 0x0d;
|
|
return if $cid != 0x4d; # No register, returns previous value
|
|
|
|
my $rate = i2c_smbus_read_byte_data($file, 0x04, NO_CACHE);
|
|
return if $rate > 0x09;
|
|
|
|
$cid = i2c_smbus_read_byte_data($file, 0xff, NO_CACHE);
|
|
$conf = i2c_smbus_read_byte_data($file, 0x03, NO_CACHE);
|
|
return if ($conf & 0x0f) != $rate;
|
|
return if $cid != $rate; # No register, returns previous value
|
|
|
|
return 5;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM95231, 1 = LM95241, 2 = LM95245, 3 = LM95234,
|
|
# 4 = LM95235, 5 = LM95233
|
|
# Registers used:
|
|
# 0x02: Status (3 unused bits)
|
|
# 0x03: Configuration (3 unused bits)
|
|
# 0x04: Conversion rate (6 unused bits, LM95234)
|
|
# 0x06: Remote diode filter control (6 unused bits, LM95231 and LM95241)
|
|
# 0x30: Remote diode model type select (6 unused bits, LM95231 and LM95241)
|
|
# 0x30: Local Temperature LSB (5 unused bits, LM95245)
|
|
# 0x30: Diode model (4 unused bits, LM95234)
|
|
# 0x33: Status register 2 (6 unused bits, LM95245)
|
|
# 0x38: Diode model status (4 unused bits, LM95234)
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: Revision ID
|
|
sub lm95231_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0xfe);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0xff);
|
|
|
|
return if $mid != 0x01; # National Semiconductor
|
|
|
|
if ($chip == 0 || $chip == 1) {
|
|
return if $chip == 0 && $cid != 0xa1; # LM95231
|
|
return if $chip == 1 && $cid != 0xa4; # LM95241
|
|
return if i2c_smbus_read_byte_data($file, 0x02) & 0x70;
|
|
return if i2c_smbus_read_byte_data($file, 0x03) & 0x89;
|
|
return if i2c_smbus_read_byte_data($file, 0x06) & 0xfa;
|
|
return if i2c_smbus_read_byte_data($file, 0x30) & 0xfa;
|
|
} elsif ($chip == 2 || $chip == 4) {
|
|
return if $chip == 4 && $cid != 0xb1; # LM95235
|
|
return if $chip == 2 && $cid != 0xb3; # LM95245
|
|
return if i2c_smbus_read_byte_data($file, 0x02) & 0x68;
|
|
return if i2c_smbus_read_byte_data($file, 0x03) & 0xa1;
|
|
return if i2c_smbus_read_byte_data($file, 0x30) & 0x1f;
|
|
return if i2c_smbus_read_byte_data($file, 0x33) & 0x3f;
|
|
} elsif ($chip == 3) {
|
|
return if $cid != 0x79; # LM95234
|
|
return if i2c_smbus_read_byte_data($file, 0x02) & 0x30;
|
|
return if i2c_smbus_read_byte_data($file, 0x03) & 0xbc;
|
|
return if i2c_smbus_read_byte_data($file, 0x04) & 0xfc;
|
|
return if i2c_smbus_read_byte_data($file, 0x30) & 0xe1;
|
|
return if i2c_smbus_read_byte_data($file, 0x38) & 0xe1;
|
|
} elsif ($chip == 5) {
|
|
return if $cid != 0x89; # LM95233
|
|
return if i2c_smbus_read_byte_data($file, 0x02) & 0x30;
|
|
return if i2c_smbus_read_byte_data($file, 0x03) & 0xbf;
|
|
return if i2c_smbus_read_byte_data($file, 0x04) & 0xfc;
|
|
return if i2c_smbus_read_byte_data($file, 0x30) & 0xf9;
|
|
return if i2c_smbus_read_byte_data($file, 0x38) & 0xf9;
|
|
}
|
|
|
|
return 6;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x03: Configuration 1
|
|
# 0x24: Configuration 2
|
|
# 0x3d: Manufacturer ID
|
|
# 0x3e: Device ID
|
|
sub adt7481_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0x3d);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $conf1 = i2c_smbus_read_byte_data($file, 0x03);
|
|
my $conf2 = i2c_smbus_read_byte_data($file, 0x24);
|
|
|
|
return if ($conf1 & 0x10) != 0;
|
|
return if ($conf2 & 0x7f) != 0;
|
|
return if $mid != 0x41; # Analog Devices
|
|
return if $cid != 0x81; # ADT7481
|
|
|
|
return 6;
|
|
}
|
|
|
|
# Chip to detect: 1 = LM63, 2 = F75363SG, 3 = LM64, 4 = LM96163
|
|
# 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 ($file, $addr, $chip) = @_;
|
|
|
|
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;
|
|
} elsif ($chip == 4) {
|
|
return if $mid != 0x01 # National Semiconductor
|
|
|| $cid != 0x49; # LM96163
|
|
return if ($conf & 0x18) != 0x00
|
|
|| ($mask & 0xa4) != 0xa4;
|
|
}
|
|
|
|
return 6;
|
|
}
|
|
|
|
# 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 ($file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0x0d);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x0e);
|
|
my $cfg;
|
|
|
|
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;
|
|
}
|
|
|
|
# Chip to detect: 0 = ADM1030, 1 = ADM1031
|
|
# 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 ($file, $addr, $chip) = @_;
|
|
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;
|
|
}
|
|
}
|
|
|
|
# Chip to detect: 0 = ADM1033, 1 = ADM1034
|
|
# Registers used:
|
|
# 0x3d: Chip ID
|
|
# 0x3e: Manufacturer ID
|
|
# 0x3f: Die revision
|
|
sub adm1034_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
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;
|
|
}
|
|
}
|
|
|
|
# Chip to detect: 0 = ADT7467/ADT7468, 1 = ADT7476, 2 = ADT7462, 3 = ADT7466,
|
|
# 4 = ADT7470
|
|
# Registers used:
|
|
# 0x3d: Chip ID
|
|
# 0x3e: Manufacturer ID
|
|
# 0x3f: Die revision
|
|
sub adt7467_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
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);
|
|
|
|
return if $mid != 0x41; # Analog Devices
|
|
|
|
if ($chip == 0) {
|
|
return if $cid != 0x68; # ADT7467/ADT7468
|
|
return if ($drev & 0xf0) != 0x70;
|
|
return 7 if $drev == 0x71 || $drev == 0x72;
|
|
return 5;
|
|
}
|
|
if ($chip == 1) {
|
|
return if $cid != 0x76; # ADT7476
|
|
return if ($drev & 0xf0) != 0x60;
|
|
return 7 if $drev >= 0x69 && $drev <= 0x6b;
|
|
return 5;
|
|
}
|
|
if ($chip == 2) {
|
|
return if $cid != 0x62; # ADT7462
|
|
return if ($drev & 0xf0) != 0x00;
|
|
return 7 if $drev == 0x04;
|
|
return 5;
|
|
}
|
|
if ($chip == 3) {
|
|
return if $cid != 0x66; # ADT7466
|
|
return if ($drev & 0xf0) != 0x00;
|
|
return 7 if $drev == 0x02;
|
|
return 5;
|
|
}
|
|
if ($chip == 4) {
|
|
return if $cid != 0x70; # ADT7470
|
|
return if ($drev & 0xf0) != 0x00;
|
|
return 7 if $drev == 0x00;
|
|
return 5;
|
|
}
|
|
}
|
|
|
|
# Chip to detect: 0 = ADT7473, 1 = ADT7475
|
|
# Registers used:
|
|
# 0x3d: Chip ID
|
|
# 0x3e: Manufacturer ID
|
|
sub adt7473_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x3d);
|
|
|
|
return if $mid != 0x41; # Analog Devices
|
|
|
|
return if $chip == 0 && $cid != 0x73; # ADT7473
|
|
return if $chip == 1 && $cid != 0x75; # ADT7475
|
|
return 5;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x3e: Manufacturer ID
|
|
# 0x3f: Chip ID
|
|
sub adt7490_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x3f);
|
|
|
|
return if $mid != 0x41; # Analog Devices
|
|
return if ($cid & 0xfc) != 0x6c; # ADT7490
|
|
return 5;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x02: Status
|
|
# 0x0a: Thyst
|
|
# 0x0b: ID
|
|
# We also rely on the fact that only the 5 LSB of the address pointer
|
|
# are considered, so registers cycle over 32 byte boundaries.
|
|
sub adt7410_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $status = i2c_smbus_read_byte_data($file, 0x02);
|
|
my $thyst = i2c_smbus_read_byte_data($file, 0x0a);
|
|
my $id = i2c_smbus_read_byte_data($file, 0x0b);
|
|
|
|
# Unused bits
|
|
return if ($status & 0x0f);
|
|
return if ($thyst & 0xf0);
|
|
|
|
# ID register
|
|
return if $id != 0xcb;
|
|
|
|
# Cycling registers
|
|
for (my $i = 2; $i < 16; $i *= 2) {
|
|
return if i2c_smbus_read_byte_data($file, 0x0a + $i * 16) != $thyst;
|
|
return if i2c_smbus_read_byte_data($file, 0x0b + $i * 16) != $id;
|
|
}
|
|
|
|
# Non-existent registers (other devices tend to have ID registers there)
|
|
return if i2c_smbus_read_byte_data($file, 0x03e) != 0;
|
|
return if i2c_smbus_read_byte_data($file, 0x03f) != 0;
|
|
return if i2c_smbus_read_byte_data($file, 0x0fe) != 0;
|
|
return if i2c_smbus_read_byte_data($file, 0x0ff) != 0;
|
|
|
|
return 3;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x4d: Device ID
|
|
# 0x4e: Manufacturer ID
|
|
# 0x4e: Silicon revision
|
|
sub adt7411_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $dev_id = i2c_smbus_read_byte_data($file, 0x4d);
|
|
my $man_id = i2c_smbus_read_byte_data($file, 0x4e);
|
|
my $revision = i2c_smbus_read_byte_data($file, 0x4f);
|
|
|
|
return if $man_id != 0x41; # Analog Devices
|
|
return if $dev_id != 0x02; # ADT7411
|
|
# The datasheet suggests that the version is in the high nibble, but
|
|
# a dump from a real ADT7411 chip shows that it is in the low nibble.
|
|
return if ($revision & 0x0f) != 0x04; # ADT7411
|
|
return 5;
|
|
}
|
|
|
|
# Chip to detect: 0 = aSC7512, 1 = aSC7611, 2 = aSC7621
|
|
# Registers used:
|
|
# 0x3e: Manufacturer ID
|
|
# 0x3f: Version
|
|
sub andigilog_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x3f);
|
|
|
|
return if $mid != 0x61; # Andigilog
|
|
|
|
return if $chip == 0 && $cid != 0x62;
|
|
return if $chip == 1 && $cid != 0x69;
|
|
return if $chip == 2 && ($cid != 0x6C && $cid != 0x6D);
|
|
return 5;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: Die Code
|
|
sub andigilog_aSC7511_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0xfe);
|
|
my $die = i2c_smbus_read_byte_data($file, 0xff);
|
|
|
|
return if $mid != 0x61; # Andigilog
|
|
return if $die != 0x00;
|
|
return 3;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM85, 1 = LM96000, 2 = ADM1027, 3 = ADT7463,
|
|
# 4 = EMC6D100/101, 5 = EMC6D102, 6 = EMC6D103,
|
|
# 7 = WPCD377I (no sensors), 8 = EMC6D103S/EMC2300
|
|
# Registers used:
|
|
# 0x3e: Vendor register
|
|
# 0x3d: Device ID register (Analog Devices only)
|
|
# 0x3f: Version/Stepping register
|
|
sub lm85_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $vendor = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $verstep = i2c_smbus_read_byte_data($file, 0x3f);
|
|
|
|
if ($chip == 0) {
|
|
return if $vendor != 0x01; # National Semiconductor
|
|
return if $verstep != 0x60 # LM85 C
|
|
&& $verstep != 0x62; # LM85 B
|
|
} elsif ($chip == 1 || $chip == 7) {
|
|
return if $vendor != 0x01; # National Semiconductor
|
|
return if $verstep != 0x68 # LM96000
|
|
&& $verstep != 0x69; # LM96000
|
|
} elsif ($chip == 2) {
|
|
return if $vendor != 0x41; # Analog Devices
|
|
return if $verstep != 0x60; # ADM1027
|
|
} elsif ($chip == 3) {
|
|
return if $vendor != 0x41; # Analog Devices
|
|
return if $verstep != 0x62 # ADT7463
|
|
&& $verstep != 0x6a; # ADT7463 C
|
|
} elsif ($chip == 4) {
|
|
return if $vendor != 0x5c; # SMSC
|
|
return if $verstep != 0x60 # EMC6D100/101 A0
|
|
&& $verstep != 0x61; # EMC6D100/101 A1
|
|
} elsif ($chip == 5) {
|
|
return if $vendor != 0x5c; # SMSC
|
|
return if $verstep != 0x65; # EMC6D102
|
|
} elsif ($chip == 6) {
|
|
return if $vendor != 0x5c; # SMSC
|
|
return if $verstep != 0x68 # EMC6D103 A0
|
|
&& $verstep != 0x69; # EMC6D103 A1
|
|
} elsif ($chip == 8) {
|
|
return if $vendor != 0x5c; # SMSC
|
|
return if $verstep != 0x6a; # EMC6D103S/EMC2300
|
|
}
|
|
|
|
if ($vendor == 0x41) { # Analog Devices
|
|
return if i2c_smbus_read_byte_data($file, 0x3d) != 0x27;
|
|
return 8;
|
|
}
|
|
|
|
if ($chip == 1 || $chip == 7) {
|
|
# Differenciate between real LM96000 and Winbond WPCD377I.
|
|
# The latter emulate the former except that it has no
|
|
# hardware monitoring function so the readings are always
|
|
# 0.
|
|
my ($in_temp, $fan, $i);
|
|
|
|
for ($i = 0; $i < 8; $i++) {
|
|
$in_temp = i2c_smbus_read_byte_data($file, 0x20 + $i);
|
|
$fan = i2c_smbus_read_byte_data($file, 0x28 + $i);
|
|
if ($in_temp != 0x00 or $fan != 0xff) {
|
|
return 7 if $chip == 1;
|
|
return;
|
|
}
|
|
}
|
|
return 7 if $chip == 7;
|
|
return;
|
|
}
|
|
|
|
return 7;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x3e: Vendor register
|
|
# 0x3f: Version/Stepping register
|
|
# 0x40: Configuration register (reserved bits + ready)
|
|
sub emc6w201_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $vendor = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $verstep = i2c_smbus_read_byte_data($file, 0x3f);
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x40);
|
|
my $stepping;
|
|
|
|
return if $vendor != 0x5c; # SMSC
|
|
return if ($verstep & 0xf0) != 0xb0; # EMC6W201
|
|
return if ($conf & 0xf4) != 0x04;
|
|
|
|
$stepping = $verstep & 0x0f;
|
|
return if $stepping > 3;
|
|
|
|
# So far we've only seen stepping 1 chips
|
|
return $stepping <= 1 ? 6 : 3;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM87, 1 = ADM1024
|
|
# Registers used:
|
|
# 0x3e: Company ID
|
|
# 0x3f: Revision
|
|
# 0x40: Configuration
|
|
sub lm87_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $rev = i2c_smbus_read_byte_data($file, 0x3f);
|
|
|
|
if ($chip == 0) {
|
|
return if $cid != 0x02; # National Semiconductor
|
|
return if ($rev & 0xfc) != 0x04; # LM87
|
|
}
|
|
if ($chip == 1) {
|
|
return if $cid != 0x41; # Analog Devices
|
|
return if ($rev & 0xf0) != 0x10; # ADM1024
|
|
}
|
|
|
|
my $cfg = i2c_smbus_read_byte_data($file, 0x40);
|
|
return if ($cfg & 0x80) != 0x00;
|
|
|
|
return 7;
|
|
}
|
|
|
|
# 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/W83667HG/W83677HG
|
|
# 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: 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 ($file, $addr, $chip) = @_;
|
|
my ($reg1, $reg2, @res);
|
|
|
|
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 != 0x88 && $reg1 != 0xa1);
|
|
return if $chip == 10 and $reg1 != 0xc1;
|
|
# Default address is 0x2d
|
|
@res = ($addr != 0x2d) ? (7) : (8);
|
|
return @res if $chip >= 9; # No subclients
|
|
|
|
$reg1 = i2c_smbus_read_byte_data($file, 0x4a);
|
|
push @res, ($reg1 & 0x07) + 0x48 unless $reg1 & 0x08;
|
|
push @res, (($reg1 & 0x70) >> 4) + 0x48
|
|
unless ($reg1 & 0x80 or $chip == 2);
|
|
return @res;
|
|
}
|
|
|
|
# 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 ($file, $addr) = @_;
|
|
my ($bank, $reg, @res);
|
|
|
|
$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 $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;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0xfc: Full I2C Address
|
|
# 0x00: Vendor ID byte selection, and bank selection(Bank 0, 1, 2)
|
|
# 0xfd: Vendor ID(Bank 0, 1, 2)
|
|
# 0xfe: Device ID(Bank 0, 1, 2)
|
|
sub w83795_detect
|
|
{
|
|
my ($bank, $reg);
|
|
my ($file, $addr) = @_;
|
|
|
|
$bank = i2c_smbus_read_byte_data($file, 0x00);
|
|
$reg = i2c_smbus_read_byte_data($file, 0xfd);
|
|
|
|
return unless (($bank & 0x80) == 0x00 and $reg == 0xa3) or
|
|
(($bank & 0x80) == 0x80 and $reg == 0x5c);
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0xfe);
|
|
return if $reg != 0x79;
|
|
|
|
# If bank 0 is selected, we can do more checks
|
|
return 6 unless ($bank & 0x07) == 0;
|
|
$reg = i2c_smbus_read_byte_data($file, 0xfc) & 0x7f;
|
|
return unless ($reg == $addr);
|
|
|
|
return 8;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x00: Bank selection (Bank 0, 1)
|
|
# 0x05: Temperature readout register LSB (Bank 0)
|
|
# 0x08: PECI temperature readout register LSB (Bank 0)
|
|
# 0x0f: Voltage readout register LSB (Bank 0)
|
|
# 0xfd: Vendor ID (Bank 0)
|
|
# 0xfe: Device ID (Bank 0)
|
|
# 0xff: Device Revision (Bank 0)
|
|
#
|
|
# Note that identification registers are not accessible in bank 1,
|
|
# and there is no usable other means to identify the chip if bank 1
|
|
# is selected. Only detect chip if bank 0 is selected.
|
|
sub nct7802_detect
|
|
{
|
|
my ($bank, $reg);
|
|
my ($file, $addr) = @_;
|
|
|
|
$bank = i2c_smbus_read_byte_data($file, 0x00);
|
|
return unless $bank == 0x00;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0xfd);
|
|
return unless $reg == 0x50;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0xfe);
|
|
return unless $reg == 0xc3;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0xff);
|
|
return unless ($reg & 0xf0) == 0x20;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0x05);
|
|
return unless ($reg & 0x1f) == 0x00;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0x08);
|
|
return unless ($reg & 0x3f) == 0x00;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0x0f);
|
|
return unless ($reg & 0x3f) == 0x00;
|
|
|
|
return 8;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x7a: Vendor ID
|
|
# 0x7b: Chip ID
|
|
# 0x7c: Device ID
|
|
# 0xff: Bank Select
|
|
#
|
|
sub nct7904_detect
|
|
{
|
|
my ($reg);
|
|
my ($file, $addr) = @_;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0x7a);
|
|
return unless $reg == 0x50;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0x7b);
|
|
return unless $reg == 0xc5;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0x7c);
|
|
return unless ($reg & 0xf0) == 0x50;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0xff);
|
|
return unless ($reg & 0xf8) == 0x00;
|
|
|
|
return 8;
|
|
}
|
|
|
|
# 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;
|
|
}
|
|
|
|
# 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 ($file, $addr) = @_;
|
|
my ($vid, $dev);
|
|
|
|
$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 unless ($dev == 0x56 && $vid == 0x9436) # ASM58
|
|
|| ($dev == 0x56 && $vid == 0x9406) # AS2K129R
|
|
|| ($dev == 0x10 && $vid == 0x5ca3);
|
|
|
|
return 5;
|
|
}
|
|
|
|
# Chip to detect: 0 = GL518SM, 1 = GL520SM
|
|
# Registers used:
|
|
# 0x00: Device ID
|
|
# 0x01: Revision ID
|
|
# 0x03: Configuration
|
|
sub gl518sm_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $reg;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file, 0x00);
|
|
return if $chip == 0 && $reg != 0x80;
|
|
return if $chip == 1 && $reg != 0x20;
|
|
|
|
return unless (i2c_smbus_read_byte_data($file, 0x03) & 0x80) == 0x00;
|
|
$reg = i2c_smbus_read_byte_data($file, 0x01);
|
|
return unless $reg == 0x00 or $reg == 0x80;
|
|
return 6;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x00: Device ID
|
|
# 0x03: Configuration
|
|
# Mediocre detection
|
|
sub gl525sm_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
return unless i2c_smbus_read_byte_data($file, 0x00) == 0x25;
|
|
return unless (i2c_smbus_read_byte_data($file, 0x03) & 0x80) == 0x00;
|
|
return 5;
|
|
}
|
|
|
|
# Chip to detect: 0 = ADM9240, 1 = DS1780, 2 = LM81
|
|
# Registers used:
|
|
# 0x3e: Company ID
|
|
# 0x40: Configuration
|
|
# 0x48: Full I2C Address
|
|
sub adm9240_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $reg;
|
|
$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;
|
|
}
|
|
|
|
# Chip to detect: 0 = ADM1022, 1 = THMC50, 2 = ADM1028, 3 = THMC51
|
|
# Registers used:
|
|
# 0x3e: Company ID
|
|
# 0x3f: Revision
|
|
# 0x40: Configuration
|
|
sub adm1022_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $reg;
|
|
$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) or
|
|
($chip == 3 and $reg == 0x49);
|
|
$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) or
|
|
($chip == 3 and $reg == 0xd0);
|
|
return 8;
|
|
}
|
|
|
|
# Chip to detect: 0 = ADM1025, 1 = NE1619
|
|
# Registers used:
|
|
# 0x3e: Company ID
|
|
# 0x3f: Revision
|
|
# 0x40: Configuration
|
|
# 0x41: Status 1
|
|
# 0x42: Status 2
|
|
sub adm1025_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $reg;
|
|
|
|
$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;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x16: Company ID
|
|
# 0x17: Revision
|
|
sub adm1026_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $reg;
|
|
$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;
|
|
}
|
|
|
|
# Chip to detect: 0 = ADM1021, 1 = ADM1021A/ADM1023, 2 = MAX1617, 3 = MAX1617A,
|
|
# 4 = THMC10, 5 = LM84, 6 = GL523, 7 = MC1066
|
|
# 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 ($file, $addr, $chip) = @_;
|
|
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.
|
|
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 == 2) {
|
|
$llo -= 256 if ($llo & 0x80);
|
|
$rlo -= 256 if ($rlo & 0x80);
|
|
return if ($llo > $lhi) or ($rlo > $rhi);
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
return ($chip <= 3) ? 7 : 5;
|
|
}
|
|
|
|
# Chip to detect: 0 = MAX1668, 1 = MAX1805, 2 = MAX1989
|
|
# Registers used:
|
|
# 0xfe: Company ID
|
|
# 0xff: Device ID
|
|
sub max1668_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
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;
|
|
}
|
|
|
|
# Chip to detect: 0 = MAX1619, 1 = MAX1618
|
|
# Registers used:
|
|
# 0xfe: Company ID
|
|
# 0xff: Device ID
|
|
# 0x02: Status
|
|
# 0x03: Configuration
|
|
# 0x04: Conversion rate
|
|
sub max1619_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
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; # Maxim
|
|
|
|
if ($chip == 0) { # MAX1619
|
|
return if $dev_id != 0x04
|
|
or ($conf & 0x03)
|
|
or ($status & 0x61)
|
|
or $convrate >= 8;
|
|
}
|
|
if ($chip == 1) { # MAX1618
|
|
return if $dev_id != 0x02
|
|
or ($conf & 0x07)
|
|
or ($status & 0x63);
|
|
}
|
|
|
|
return 7;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x28: User ID
|
|
# 0x29: User ID2
|
|
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;
|
|
}
|
|
|
|
# Registers used:
|
|
# 0x00: Configuration
|
|
# 0x48: Full I2C Address
|
|
# 0x58: Mfr ID
|
|
# 0x5b: Device ID
|
|
sub it8712_i2c_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $reg;
|
|
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 i2c_smbus_read_byte_data($file, 0x5b) != 0x12;
|
|
return 7 + ($addr == 0x2d);
|
|
}
|
|
|
|
# Registers used:
|
|
# 0-63: SPD Data and Checksum (up to DDR2)
|
|
# 0-127: SPD data and CRC (DDR3)
|
|
sub eeprom_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $device_type = i2c_smbus_read_byte_data($file, 2);
|
|
my $checksum = 0;
|
|
|
|
# Check the checksum or CRC16 for validity
|
|
if ($device_type >= 1 and $device_type <= 8) {
|
|
for (my $i = 0; $i < 63; $i++) {
|
|
$checksum += i2c_smbus_read_byte_data($file, $i);
|
|
}
|
|
$checksum &= 0xff;
|
|
|
|
return 8 if $checksum == i2c_smbus_read_byte_data($file, 63);
|
|
} elsif ($device_type >= 9 && $device_type <= 12) {
|
|
# see JEDEC 21-C 4.1.2.11 2.4
|
|
my $crc_coverage = i2c_smbus_read_byte_data($file, 0);
|
|
$crc_coverage = ($crc_coverage & 0x80) ? 117 : 126;
|
|
for (my $i = 0; $i < $crc_coverage; $i++) {
|
|
$checksum ^= i2c_smbus_read_byte_data($file, $i) << 8;
|
|
for (my $bit = 0; $bit < 8; $bit++) {
|
|
if ($checksum & 0x8000) {
|
|
$checksum = ($checksum << 1) ^ 0x1021;
|
|
} else {
|
|
$checksum <<= 1;
|
|
}
|
|
}
|
|
}
|
|
$checksum &= 0xffff;
|
|
|
|
return 8 if ($checksum & 0xff) == i2c_smbus_read_byte_data($file, 126) and
|
|
($checksum >> 8) == i2c_smbus_read_byte_data($file, 127);
|
|
|
|
# note: if bit 7 of byte 32 is set, a jc42 sensor is at $addr-0x38
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
# 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;
|
|
}
|
|
|
|
# Chip to detect: 0 = Poseidon I, 1 = Poseidon II, 2 = Scylla,
|
|
# 3 = Hermes, 4 = Heimdal, 5 = Heracles
|
|
# Registers used:
|
|
# 0x00-0x02: Identification (3 capital ASCII letters)
|
|
sub fsc_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $id;
|
|
|
|
$id = chr(i2c_smbus_read_byte_data($file, 0x00))
|
|
. chr(i2c_smbus_read_byte_data($file, 0x01))
|
|
. chr(i2c_smbus_read_byte_data($file, 0x02));
|
|
|
|
return if $chip == 0 and $id ne 'PEG'; # Pegasus? aka Poseidon I
|
|
return if $chip == 1 and $id ne 'POS'; # Poseidon II
|
|
return if $chip == 2 and $id ne 'SCY'; # Scylla
|
|
return if $chip == 3 and $id ne 'HER'; # Hermes
|
|
return if $chip == 4 and $id ne 'HMD'; # Heimdal
|
|
return if $chip == 5 and $id ne 'HRC'; # Heracles
|
|
return if $chip == 6 and $id ne 'HDS'; # Hades
|
|
return if $chip == 7 and $id ne 'SYL'; # Syleus
|
|
|
|
return 8;
|
|
}
|
|
|
|
# Chip to detect: FTS Teutates
|
|
# Registers used:
|
|
# 0x00: Device ID
|
|
# 0x01: Revision
|
|
# 0x0C: Device Detection Register 1
|
|
# 0x0D: Device Detection Register 2
|
|
# 0x0E: Device Detection Register 3
|
|
sub fts_detect
|
|
{
|
|
my ($file) = @_;
|
|
|
|
# Device ID must be 0x11
|
|
# 0x10 == Baseboard Management Controller
|
|
# 0x01 == Teutates
|
|
return unless i2c_smbus_read_byte_data($file, 0x00) == 0x11;
|
|
|
|
# Revision must be greater or equal 0x2b to use device detection
|
|
return unless i2c_smbus_read_byte_data($file, 0x01) >= 0x2b;
|
|
|
|
# Device Detection Registers must be 0x17, 0x34, 0x54
|
|
return unless i2c_smbus_read_byte_data($file, 0x0C) == 0x17
|
|
and i2c_smbus_read_byte_data($file, 0x0D) == 0x34
|
|
and i2c_smbus_read_byte_data($file, 0x0E) == 0x54;
|
|
|
|
return 8;
|
|
}
|
|
|
|
# Chip to detect: 0 = LM93, 1 = LM94
|
|
# Registers used:
|
|
# 0x3E: Manufacturer ID
|
|
# 0x3F: Version/Stepping
|
|
sub lm93_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my ($man_id, $dev_id);
|
|
|
|
$man_id = i2c_smbus_read_byte_data($file, 0x3E);
|
|
$dev_id = i2c_smbus_read_byte_data($file, 0x3F);
|
|
|
|
if ($chip == 0) {
|
|
return unless $man_id == 0x01 # National Semiconductor
|
|
and $dev_id == 0x73; # LM93
|
|
}
|
|
if ($chip == 1) {
|
|
return unless $man_id == 0x01 # National Semiconductor
|
|
and $dev_id >= 0x79
|
|
and $dev_id <= 0x7A; # LM94
|
|
}
|
|
return 5;
|
|
}
|
|
|
|
# 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;
|
|
}
|
|
|
|
# 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);
|
|
}
|
|
|
|
# Chip to detect: 1 = DME1737, 2 = SCH5027
|
|
# 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 ($file, $addr, $chip) = @_;
|
|
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);
|
|
}
|
|
|
|
# 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
|
|
# Registers used:
|
|
# 0x5A-0x5B: Chip ID
|
|
# 0x5D-0x5E: Vendor ID
|
|
sub fintek_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
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;
|
|
}
|
|
|
|
# Chips to detect: 0 = EMC1023, 1 = EMC1043, 2 = EMC1053, 3 = EMC1063
|
|
# Registers used:
|
|
# 0xed: Device ID register
|
|
# 0xfe: Vendor ID register
|
|
# 0xff: Revision register
|
|
sub emc1023_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $dev_id = i2c_smbus_read_byte_data($file, 0xed);
|
|
my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
|
|
my $rev = i2c_smbus_read_byte_data($file, 0xff);
|
|
|
|
return unless $man_id == 0x5d; # SMSC
|
|
return unless $rev <= 1;
|
|
|
|
if ($chip == 0) {
|
|
return if ($addr == 0x4c) && ($dev_id != 0x04); # EMC1023-1
|
|
return if ($addr == 0x4d) && ($dev_id != 0x05); # EMC1023-2
|
|
return if ($addr == 0x48) && ($dev_id != 0x06); # EMC1023-3
|
|
return if ($addr == 0x49) && ($dev_id != 0x07); # EMC1023-4
|
|
} elsif ($chip == 1) {
|
|
if ($addr == 0x4c) { # EMC1043-1, EMC1043-5
|
|
return unless ($dev_id == 0x0c) || ($dev_id == 0x2c);
|
|
}
|
|
return if ($addr == 0x4d) && ($dev_id != 0x0d); # EMC1043-2
|
|
return if ($addr == 0x48) && ($dev_id != 0x0e); # EMC1043-3
|
|
return if ($addr == 0x49) && ($dev_id != 0x0f); # EMC1043-4
|
|
} elsif ($chip == 2) {
|
|
return if ($addr == 0x4c) && ($dev_id != 0x3c); # EMC1053-1
|
|
return if ($addr == 0x4d) && ($dev_id != 0x3d); # EMC1053-2
|
|
return if ($addr == 0x48) && ($dev_id != 0x3e); # EMC1053-3
|
|
return if ($addr == 0x49) && ($dev_id != 0x3f); # EMC1053-4
|
|
} elsif ($chip == 3) {
|
|
return if ($addr == 0x4c) && ($dev_id != 0x30); # EMC1063-1
|
|
return if ($addr == 0x4d) && ($dev_id != 0x31); # EMC1063-2
|
|
return if ($addr == 0x48) && ($dev_id != 0x32); # EMC1063-3
|
|
return if ($addr == 0x49) && ($dev_id != 0x33); # EMC1063-4
|
|
}
|
|
|
|
return 7;
|
|
}
|
|
|
|
# Chip to detect: 0 = EMC1403, 1 = EMC1404, 2 = EMC2103, 3 = EMC1423,
|
|
# 4 = EMC1002, 5 = EMC1033, 6 = EMC1046, 7 = EMC1047, 8 = EMC1072,
|
|
# 9 = EMC1073, 10 = EMC1074, 11 = EMC1402, 12 = EMC1424,
|
|
# 13 = EMC2104, 14 = EMC1422
|
|
# Registers used:
|
|
# 0xfd: Device ID register
|
|
# 0xfe: Vendor ID register
|
|
# 0xff: Revision register
|
|
sub emc1403_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my $dev_id = i2c_smbus_read_byte_data($file, 0xfd);
|
|
my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
|
|
my $rev = i2c_smbus_read_byte_data($file, 0xff);
|
|
|
|
return unless $man_id == 0x5d; # SMSC
|
|
|
|
if ($chip == 0) { # EMC1403
|
|
return unless $dev_id == 0x21;
|
|
return unless $rev == 0x01 || $rev == 0x04;
|
|
} elsif ($chip == 1) { # EMC1404
|
|
return unless $dev_id == 0x25;
|
|
return unless $rev == 0x01 || $rev == 0x04;
|
|
} elsif ($chip == 2) { # EMC2103
|
|
return unless ($dev_id == 0x24) || ($dev_id == 0x26);
|
|
return unless $rev == 0x01;
|
|
} elsif ($chip == 3) { # EMC1423
|
|
return unless $dev_id == 0x23;
|
|
return unless $rev == 0x01 || $rev == 0x04;
|
|
} elsif ($chip == 4) { # EMC1002
|
|
return unless ($dev_id == 0x02) || ($dev_id == 0x03);
|
|
return unless $rev == 0x01;
|
|
} elsif ($chip == 5) { # EMC1033
|
|
return unless ($dev_id == 0x0a) || ($dev_id == 0x0b);
|
|
return unless $rev == 0x01;
|
|
} elsif ($chip == 6) { # EMC1046
|
|
return unless $dev_id == 0x1a;
|
|
return unless $rev == 0x01;
|
|
} elsif ($chip == 7) { # EMC1047
|
|
return unless $dev_id == 0x1c;
|
|
return unless $rev == 0x01;
|
|
} elsif ($chip == 8) { # EMC1072
|
|
return unless $dev_id == 0x20;
|
|
return unless $rev == 0x03;
|
|
} elsif ($chip == 9) { # EMC1073
|
|
return unless $dev_id == 0x21;
|
|
return unless $rev == 0x03;
|
|
} elsif ($chip == 10) { # EMC1074
|
|
return unless $dev_id == 0x25;
|
|
return unless $rev == 0x03;
|
|
} elsif ($chip == 11) { # EMC1402
|
|
return unless $dev_id == 0x20;
|
|
return unless $rev == 0x01 || $rev == 0x04;
|
|
} elsif ($chip == 12) { # EMC1424
|
|
return unless $dev_id == 0x27;
|
|
return unless $rev == 0x01 || $rev == 0x04;
|
|
} elsif ($chip == 13) { # EMC2104
|
|
return unless $dev_id == 0x1d;
|
|
return unless $rev == 0x02;
|
|
} elsif ($chip == 14) { # EMC1422
|
|
return unless $dev_id == 0x22;
|
|
return unless $rev == 0x01 || $rev == 0x04;
|
|
}
|
|
|
|
return 6;
|
|
}
|
|
|
|
# Chip to detect: 0 = W83L784R/AR/G, 1 = W83L785R/G, 2 = W83L786NR/NG/R/G,
|
|
# 3 = W83L785TS-S
|
|
# 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 ($file, $addr, $chip) = @_;
|
|
my ($reg, @res);
|
|
|
|
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 if $chip == 3 and $reg != 0x70;
|
|
|
|
return 8 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;
|
|
}
|
|
|
|
# Chip to detect: MAX6639
|
|
# Registers used:
|
|
# 0x3d: Device ID
|
|
# 0x3e: Manufacturer ID
|
|
# 0x3f: Chip revision
|
|
sub max6639_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my ($man_id, $dev_id, $rev);
|
|
|
|
$dev_id = i2c_smbus_read_byte_data($file, 0x3d);
|
|
$man_id = i2c_smbus_read_byte_data($file, 0x3e);
|
|
$rev = i2c_smbus_read_byte_data($file, 0x3f);
|
|
|
|
return unless $man_id == 0x4d; # Maxim
|
|
return unless $dev_id == 0x58; # MAX6639
|
|
return unless $rev == 0x00;
|
|
|
|
return 6;
|
|
}
|
|
|
|
# Chip to detect: MAX6642
|
|
# Registers used:
|
|
# 0x02: Status register
|
|
# 0x03: Configuration register
|
|
# 0xfe: Manufacturer ID
|
|
# 0x04, 0x06, 0xff: No registers
|
|
# We use the 0x04, 0x06 and 0xff addresses (unused) to improve reliability.
|
|
# These are not real registers and will always return the last returned value.
|
|
# This isn't documented.
|
|
sub max6642_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my ($man_id, $conf, $status);
|
|
|
|
$man_id = i2c_smbus_read_byte_data($file, 0xfe);
|
|
return unless $man_id == 0x4d; # Maxim
|
|
return if i2c_smbus_read_byte_data($file, 0x04, NO_CACHE) != $man_id;
|
|
return if i2c_smbus_read_byte_data($file, 0x06, NO_CACHE) != $man_id;
|
|
return if i2c_smbus_read_byte_data($file, 0xff, NO_CACHE) != $man_id;
|
|
|
|
$status = i2c_smbus_read_byte_data($file, 0x02);
|
|
# Bit 5, 3, 1 and 0 should be zero
|
|
return unless ($status & 0x2b) == 0x00;
|
|
return if i2c_smbus_read_byte_data($file, 0x04, NO_CACHE) != $status;
|
|
return if i2c_smbus_read_byte_data($file, 0x06, NO_CACHE) != $status;
|
|
return if i2c_smbus_read_byte_data($file, 0xff, NO_CACHE) != $status;
|
|
|
|
$conf = i2c_smbus_read_byte_data($file, 0x03);
|
|
# The 4 lower bits should be zero
|
|
return unless ($conf & 0x0f) == 0x00;
|
|
|
|
return 5;
|
|
}
|
|
|
|
sub max6655_detect
|
|
{
|
|
my ($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;
|
|
}
|
|
|
|
# Chip to detect: 0 = STTS424, 1 = SE97/SE97B, 2 = SE98, 3 = ADT7408,
|
|
# 4 = TS3000/TSE2002, 5 = MAX6604, 6 = MCP98242,
|
|
# 7 = MCP98243, 8 = MCP9843, 9 = CAT6095 / CAT34TS02,
|
|
# 10 = STTS424E, 11 = STTS2002, 12 = STTS3000
|
|
# 13 = MCP9804, 14 = AT30TS00, 15 = MCP98244,
|
|
# 16 = TSE2004, 17 = TS3001, 18 = MCP9808,
|
|
# 19 = CAT34TS02C, 20 = Giantec GT30TS00,
|
|
# 21 = CAT34TS04
|
|
# Registers used:
|
|
# 0x00: Capabilities
|
|
# 0x01: Configuration
|
|
# 0x06: Manufacturer ID
|
|
# 0x07: Device ID
|
|
sub jedec_JC42_4_detect
|
|
{
|
|
my ($file, $addr, $chip) = @_;
|
|
my ($reg, $manid, $devid);
|
|
|
|
# We test the MSB only at first, because not all chips enjoy
|
|
# word access.
|
|
|
|
# Check for unused bits
|
|
$reg = i2c_smbus_read_byte_data($file, 0x00);
|
|
return if $reg & 0xff;
|
|
$reg = i2c_smbus_read_byte_data($file, 0x01);
|
|
return if $reg & 0xf8;
|
|
|
|
# Check for manufacturer and device ID
|
|
$manid = i2c_smbus_read_byte_data($file, 0x06);
|
|
$devid = i2c_smbus_read_byte_data($file, 0x07);
|
|
|
|
if ($chip == 0) {
|
|
return unless $manid == 0x10; # STMicrolectronics
|
|
return unless $devid == 0x01; # STTS424
|
|
} elsif ($chip == 1) {
|
|
return unless $manid == 0x11; # NXP
|
|
return unless $devid == 0xa2; # SE97
|
|
} elsif ($chip == 2) {
|
|
return unless $manid == 0x11; # NXP
|
|
return unless $devid == 0xa1; # SE98
|
|
} elsif ($chip == 3) {
|
|
return unless $manid == 0x11; # ADT
|
|
return unless $devid == 0x08; # ADT7408
|
|
} elsif ($chip == 4) {
|
|
return unless $manid == 0x00; # IDT
|
|
return unless $devid == 0x29; # TS3000/TSE2002
|
|
} elsif ($chip == 5) {
|
|
return unless $manid == 0x00; # MAXIM
|
|
return unless $devid == 0x3e; # MAX6604
|
|
} elsif ($chip == 6) {
|
|
return unless $manid == 0x00; # MCP
|
|
return unless $devid == 0x20; # MCP98242
|
|
} elsif ($chip == 7) {
|
|
return unless $manid == 0x00; # MCP
|
|
return unless $devid == 0x21; # MCP98243
|
|
} elsif ($chip == 8) {
|
|
return unless $manid == 0x00; # MCP
|
|
return unless $devid == 0x00; # MCP9843
|
|
} elsif ($chip == 9) {
|
|
return unless $manid == 0x1b; # ONS
|
|
return unless $devid == 0x08; # CAT6095 / CAT34TS02
|
|
} elsif ($chip == 10) {
|
|
return unless $manid == 0x10; # STMicrolectronics
|
|
return unless $devid == 0x00; # STTS424E02
|
|
} elsif ($chip == 11) {
|
|
return unless $manid == 0x10; # STMicrolectronics
|
|
return unless $devid == 0x03; # STTS2002
|
|
} elsif ($chip == 12) {
|
|
return unless $manid == 0x10; # STMicrolectronics
|
|
return unless $devid == 0x02; # STTS3000
|
|
} elsif ($chip == 13) {
|
|
return unless $manid == 0x00; # MCP
|
|
return unless $devid == 02; # MCP9804
|
|
} elsif ($chip == 14) {
|
|
return unless $manid == 0x00; # Atmel
|
|
return unless $devid == 0x82; # AT30TS00
|
|
} elsif ($chip == 15) {
|
|
return unless $manid == 0x00; # MCP
|
|
return unless $devid == 0x22; # MCP98244
|
|
} elsif ($chip == 18) {
|
|
return unless $manid == 0x00; # MCP
|
|
return unless $devid == 0x04; # MCP9808
|
|
} elsif ($chip == 19) {
|
|
return unless $manid == 0x1b; # ON
|
|
return unless $devid == 0x0a; # CAT34TS02C
|
|
} elsif ($chip == 20) {
|
|
return unless $manid == 0x1c || $manid == 0x13; # Giantec
|
|
return unless $devid == 0x22; # GT30TS00
|
|
} elsif ($chip == 21) {
|
|
return unless $manid == 0x1b; # ONS
|
|
return unless $devid == 0x22; # CAT34TS04
|
|
}
|
|
|
|
# Now, do it all again with words. Note that we get
|
|
# the MSB first, so all value checks are byte-swapped.
|
|
|
|
# Check for unused bits
|
|
$reg = i2c_smbus_read_word_data($file, 0x00);
|
|
return if $reg < 0 || $reg & 0x00ff;
|
|
|
|
$manid = i2c_smbus_read_word_data($file, 0x06);
|
|
$devid = i2c_smbus_read_word_data($file, 0x07);
|
|
return if $manid < 0 || $devid < 0;
|
|
if ($chip == 0) {
|
|
return unless $manid == 0x4a10; # STMicrolectronics
|
|
return unless ($devid & 0xfeff) == 0x0001; # STTS424
|
|
} elsif ($chip == 1) {
|
|
return unless $manid == 0x3111; # NXP
|
|
return unless ($devid & 0xfcff) == 0x00a2; # SE97
|
|
} elsif ($chip == 2) {
|
|
return unless $manid == 0x3111; # NXP
|
|
return unless ($devid & 0xfcff) == 0x00a1; # SE98
|
|
} elsif ($chip == 3) {
|
|
return unless $manid == 0xd411; # ADT
|
|
return unless $devid == 0x0108; # ADT7408
|
|
} elsif ($chip == 4) {
|
|
return unless $manid == 0xb300; # IDT
|
|
return unless ($devid & 0x00ff) == 0x0029; # TS3000/TSE2002
|
|
} elsif ($chip == 5) {
|
|
return unless $manid == 0x4d00; # MAXIM
|
|
return unless $devid == 0x003e; # MAX6604
|
|
} elsif ($chip == 6) {
|
|
return unless $manid == 0x5400; # MCP
|
|
return unless ($devid & 0xfcff) == 0x0020; # MCP98242
|
|
} elsif ($chip == 7) {
|
|
return unless $manid == 0x5400; # MCP
|
|
return unless ($devid & 0xfcff) == 0x0021; # MCP98243
|
|
} elsif ($chip == 8) {
|
|
return unless $manid == 0x5400; # MCP
|
|
return unless ($devid & 0xfcff) == 0x0000; # MCP9843
|
|
} elsif ($chip == 9) {
|
|
return unless $manid == 0x091b; # ONS
|
|
return unless ($devid & 0xe0ff) == 0x0008; # CAT6095 / CAT34TS02
|
|
} elsif ($chip == 10) {
|
|
return unless $manid == 0x4a10; # STMicrolectronics
|
|
return unless ($devid & 0xfeff) == 0x0000; # STTS424E02
|
|
} elsif ($chip == 11) {
|
|
return unless $manid == 0x4a10; # STMicrolectronics
|
|
return unless ($devid & 0xfeff) == 0x0003; # STTS2002
|
|
} elsif ($chip == 12) {
|
|
return unless $manid == 0x4a10; # STMicrolectronics
|
|
return unless ($devid & 0xfeff) == 0x0002; # STTS3000
|
|
} elsif ($chip == 13) {
|
|
return unless $manid == 0x5400; # MCP
|
|
return unless ($devid & 0xfcff) == 0x0002; # MCP9804
|
|
} elsif ($chip == 14) {
|
|
return unless $manid == 0x1f00; # Atmel
|
|
return unless ($devid & 0xfeff) == 0x0082; # AT30TS00
|
|
} elsif ($chip == 15) {
|
|
return unless $manid == 0x5400; # MCP
|
|
return unless ($devid & 0xfcff) == 0x0022; # MCP98244
|
|
} elsif ($chip == 16) {
|
|
return unless $manid == 0xb300; # IDT
|
|
return unless ($devid & 0x00ff) == 0x0022; # TSE2004
|
|
} elsif ($chip == 17) {
|
|
return unless $manid == 0xb300; # IDT
|
|
return unless ($devid & 0x00ff) == 0x0030; # TS3001
|
|
} elsif ($chip == 18) {
|
|
return unless $manid == 0x5400; # MCP
|
|
return unless ($devid & 0xfcff) == 0x0004; # MCP9808
|
|
} elsif ($chip == 19) {
|
|
return unless $manid == 0x091b; # ONS
|
|
return unless ($devid & 0xf0ff) == 0x000a; # CAT34TS02C
|
|
} elsif ($chip == 20) {
|
|
return unless $manid == 0x681c || $manid == 0x2d13; # Giantec
|
|
return unless ($devid & 0x00ff) == 0x0022; # GT30TS00
|
|
} elsif ($chip == 21) {
|
|
return unless $manid == 0x091b; # ONS
|
|
return unless ($devid & 0xf0ff) == 0x0022; # CAT34TS04
|
|
}
|
|
|
|
return 5;
|
|
}
|
|
|
|
# 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 ($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;
|
|
}
|
|
|
|
# All ISA detection functions below take at least 1 parameter:
|
|
# $_[0]: Address
|
|
# Some of these functions which can detect more than one type of device,
|
|
# take a second parameter:
|
|
# $_[1]: Chip to detect
|
|
|
|
# Chip to detect: 0 = LM78, 2 = LM79
|
|
sub lm78_isa_detect
|
|
{
|
|
my ($addr, $chip) = @_;
|
|
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;
|
|
}
|
|
|
|
return unless (isa_read_i5d6($addr, 0x40) & 0x80) == 0x00;
|
|
my $reg = isa_read_i5d6($addr, 0x49);
|
|
return if $chip == 0 && ($reg != 0x00 && $reg != 0x20 && $reg != 0x40);
|
|
return if $chip == 2 && ($reg & 0xfe) != 0xc0;
|
|
|
|
# Explicitly prevent misdetection of Winbond chips
|
|
$reg = isa_read_i5d6($addr, 0x4f);
|
|
return if $reg == 0xa3 || $reg == 0x5c;
|
|
|
|
# Explicitly prevent misdetection of ITE chips
|
|
$reg = isa_read_i5d6($addr, 0x58);
|
|
return if $reg == 0x90;
|
|
|
|
return 6;
|
|
}
|
|
|
|
# Chip to detect: 0 = W83781D, 1 = W83782D
|
|
sub w83781d_isa_detect
|
|
{
|
|
my ($addr, $chip) = @_;
|
|
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;
|
|
}
|
|
|
|
$reg1 = isa_read_i5d6($addr, 0x4e);
|
|
$reg2 = isa_read_i5d6($addr, 0x4f);
|
|
return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xa3) or
|
|
(($reg1 & 0x80) == 0x80 and $reg2 == 0x5c);
|
|
return unless ($reg1 & 0x07) == 0x00;
|
|
$reg1 = isa_read_i5d6($addr, 0x58);
|
|
return if $chip == 0 && ($reg1 & 0xfe) != 0x10;
|
|
return if $chip == 1 && ($reg1 & 0xfe) != 0x30;
|
|
|
|
return 8;
|
|
}
|
|
|
|
########
|
|
# IPMI #
|
|
########
|
|
|
|
# Returns: number of IPMI interfaces found
|
|
sub ipmi_from_smbios
|
|
{
|
|
my ($version, $if, @ipmi_if);
|
|
my $ipmi_driver = "to-be-written"; # ipmisensors
|
|
|
|
return 0 unless check_dmidecode_version();
|
|
|
|
# Parse the output of dmidecode into an array of IPMI interfaces.
|
|
# Each entry is a hash with the following keys: type and addr.
|
|
$if = -1;
|
|
open(local *DMIDECODE, "dmidecode -t 38 2>/dev/null |") or return 0;
|
|
while (<DMIDECODE>) {
|
|
if (m/^IPMI Device Information/) {
|
|
$if++;
|
|
next;
|
|
}
|
|
next unless $if >= 0;
|
|
|
|
if (m/^\tInterface Type: (.*)$/) {
|
|
$ipmi_if[$if]->{type} = "IPMI BMC $1";
|
|
$ipmi_if[$if]->{type} =~ s/ \(.*//;
|
|
next;
|
|
}
|
|
if (m/^\tBase Address: (0x[0-9A-Fa-f]+) \(I\/O\)$/) {
|
|
$ipmi_if[$if]->{addr} = oct($1);
|
|
next;
|
|
}
|
|
}
|
|
close(DMIDECODE);
|
|
|
|
foreach $if (@ipmi_if) {
|
|
if (exists $if->{addr}) {
|
|
printf("\%-60s", sprintf("Found `\%s' at 0x\%x... ",
|
|
$if->{type}, $if->{addr}));
|
|
} else {
|
|
printf("\%-60s", sprintf("Found `\%s'... ",
|
|
$if->{type}));
|
|
}
|
|
print "Success!\n".
|
|
" (confidence 8, driver `$ipmi_driver')\n";
|
|
my $new_hash = {
|
|
conf => 8,
|
|
isa_addr => $if->{addr} || 0,
|
|
chipname => $if->{type},
|
|
};
|
|
add_isa_to_chips_detected($ipmi_driver, $new_hash);
|
|
}
|
|
|
|
return scalar @ipmi_if;
|
|
}
|
|
|
|
# We simply look for a register at standard locations.
|
|
# For KCS, use the STATUS register. For SMIC, use the FLAGS register.
|
|
# Incidentally they live at the same offset.
|
|
sub ipmi_detect
|
|
{
|
|
my ($addr) = @_;
|
|
return if inb($addr + 3) == 0xff;
|
|
return 4;
|
|
}
|
|
|
|
###################
|
|
# ALIAS DETECTION #
|
|
###################
|
|
|
|
# These functions take at least 3 parameters:
|
|
# $_[0]: ISA/LPC address
|
|
# $_[1]: I2C file handle
|
|
# $_[2]: I2C address
|
|
# Some of these functions may take extra parameters.
|
|
# They return 1 if both devices are the same, 0 if not.
|
|
|
|
# Extra parameters:
|
|
# $_[3]: First limit register to compare
|
|
# $_[4]: Last limit register to compare
|
|
sub winbond_alias_detect
|
|
{
|
|
my ($isa_addr, $file, $i2c_addr, $first, $last) = @_;
|
|
my $i;
|
|
|
|
return 0 unless isa_read_i5d6($isa_addr, 0x48) == $i2c_addr;
|
|
for ($i = $first; $i <= $last; $i++) {
|
|
return 0 unless isa_read_i5d6($isa_addr, $i) ==
|
|
i2c_smbus_read_byte_data($file, $i);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
############################
|
|
# PCI CHIP / CPU DETECTION #
|
|
############################
|
|
|
|
sub sis5595_pci_detect
|
|
{
|
|
return unless exists $pci_list{'1039:0008'};
|
|
return 9;
|
|
}
|
|
|
|
sub via686a_pci_detect
|
|
{
|
|
return unless exists $pci_list{'1106:3057'};
|
|
return 9;
|
|
}
|
|
|
|
sub via8231_pci_detect
|
|
{
|
|
return unless exists $pci_list{'1106:8235'};
|
|
return 9;
|
|
}
|
|
|
|
sub amd_pci_detect
|
|
{
|
|
my $dev_id;
|
|
while (defined($dev_id = shift)) {
|
|
return 9 if exists $pci_list{"1022:$dev_id"};
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
sub hygon_pci_detect
|
|
{
|
|
my $dev_id;
|
|
while (defined($dev_id = shift)) {
|
|
return 9 if exists $pci_list{"1d94:$dev_id"};
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
sub fam10h_pci_detect
|
|
{
|
|
return unless exists $pci_list{'1022:1203'};
|
|
|
|
# Errata 319 (Inaccurate Temperature Measurement) affects
|
|
# revisions DR-BA, DR-B2 and DR-B3, all have model number = 2.
|
|
# Revisions RB-C2 and HY-D0 are also affected.
|
|
my $probecpu;
|
|
foreach $probecpu (@cpu) {
|
|
next unless $probecpu->{vendor_id} eq 'AuthenticAMD' &&
|
|
$probecpu->{'cpu family'} == 0x10;
|
|
|
|
next if $probecpu->{model} < 4; # DR-B*
|
|
next if $probecpu->{model} == 8; # HY-D0
|
|
if ($probecpu->{model} == 4 && $probecpu->{stepping} <= 2) { # RB-C2
|
|
my @dram_cfg = split /\n/, `setpci -d 1022:1202 94.L 2>/dev/null`;
|
|
next unless @dram_cfg >= 1;
|
|
next unless hex($dram_cfg[0]) & 0x00000100; # DDR3
|
|
}
|
|
|
|
return 9;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
sub intel_amb_detect
|
|
{
|
|
if ((exists $pci_list{'8086:25f0'}) || # Intel 5000
|
|
(exists $pci_list{'8086:4030'})) { # Intel 5400
|
|
return 9;
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub intel_5500_detect
|
|
{
|
|
return unless exists $pci_list{'8086:3438'};
|
|
return 5;
|
|
}
|
|
|
|
sub cpuid
|
|
{
|
|
my ($cpu_nr, $eax) = @_;
|
|
|
|
sysopen(CPUID, "/dev/cpu/$cpu_nr/cpuid", O_RDONLY) or return;
|
|
binmode CPUID;
|
|
sysseek(CPUID, $eax, SEEK_SET)
|
|
or die "Cannot seek /dev/cpu/$cpu_nr/cpuid";
|
|
sysread(CPUID, my $data, 16)
|
|
or die "Cannot read /dev/cpu/$cpu_nr/cpuid";
|
|
close CPUID;
|
|
|
|
return unpack("L4", $data);
|
|
}
|
|
|
|
sub coretemp_detect
|
|
{
|
|
my $probecpu;
|
|
|
|
foreach $probecpu (@cpu) {
|
|
next unless $probecpu->{vendor_id} eq 'GenuineIntel' &&
|
|
$probecpu->{'cpuid level'} >= 6;
|
|
|
|
# Now we check for the DTS flag
|
|
my @regs = cpuid($probecpu->{nr}, 6);
|
|
return unless @regs == 4;
|
|
return 9 if ($regs[0] & (1 << 0)); # eax, bit 0
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub via_c7_detect
|
|
{
|
|
my $probecpu;
|
|
foreach $probecpu (@cpu) {
|
|
if ($probecpu->{vendor_id} eq 'CentaurHauls' &&
|
|
$probecpu->{'cpu family'} == 6 &&
|
|
($probecpu->{model} == 0xa ||
|
|
$probecpu->{model} == 0xd)) {
|
|
return 9;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
sub via_nano_detect
|
|
{
|
|
my $probecpu;
|
|
foreach $probecpu (@cpu) {
|
|
if ($probecpu->{vendor_id} eq 'CentaurHauls' &&
|
|
$probecpu->{'cpu family'} == 6 &&
|
|
$probecpu->{model} == 0xf) {
|
|
return 9;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
#################
|
|
# SPECIAL MODES #
|
|
#################
|
|
|
|
sub show_i2c_stats
|
|
{
|
|
my ($chip, $addr, %histo, $chips);
|
|
|
|
# Gather the data
|
|
foreach $chip (@chip_ids) {
|
|
next unless defined $chip->{i2c_addrs};
|
|
$chips++;
|
|
foreach my $addr (@{$chip->{i2c_addrs}}) {
|
|
$histo{$addr}++;
|
|
}
|
|
}
|
|
|
|
# Display the data
|
|
printf "\%d I2C chips known, \%d I2C addresses probed\n\n",
|
|
$chips, scalar keys %histo;
|
|
print " 0 1 2 3 4 5 6 7 8 9 a b c d e f\n".
|
|
"00: ";
|
|
for (my $addr = 0x03; $addr <= 0x77; $addr++) {
|
|
printf("\n\%02x:", $addr) if ($addr % 16) == 0;
|
|
if (defined $histo{$addr}) {
|
|
printf ' %02d', $histo{$addr};
|
|
} else {
|
|
print ' --';
|
|
}
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
################
|
|
# 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", $i2c_adapters[$data->{i2c_devnr}]->{name};
|
|
printf " Busdriver `%s', I2C address 0x%02x",
|
|
$i2c_adapters[$data->{i2c_devnr}]->{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};
|
|
}
|
|
}
|
|
|
|
sub generate_modprobes
|
|
{
|
|
my ($driver, $detection, $adap);
|
|
my ($configfile, %bus_modules, %hwmon_modules);
|
|
|
|
foreach $driver (keys %chips_detected) {
|
|
foreach $detection (@{$chips_detected{$driver}}) {
|
|
# Tag adapters which host hardware monitoring chips we want to access
|
|
if (exists $detection->{i2c_devnr}
|
|
&& !exists $detection->{isa_addr}) {
|
|
$i2c_adapters[$detection->{i2c_devnr}]->{used}++;
|
|
}
|
|
|
|
# i2c-isa is loaded automatically (as a dependency)
|
|
# since 2.6.14, and will soon be gone.
|
|
if (exists $detection->{isa_addr}
|
|
&& !kernel_version_at_least(2, 6, 18)) {
|
|
$bus_modules{"i2c-isa"}++
|
|
}
|
|
}
|
|
if ($driver eq "ipmisensors") {
|
|
$bus_modules{"ipmi-si"}++;
|
|
}
|
|
}
|
|
|
|
# Handle aliases
|
|
# As of kernel 2.6.28, alias detection is handled by kernel drivers
|
|
# directly, so module options are no longer needed.
|
|
unless (kernel_version_at_least(2, 6, 28)) {
|
|
foreach $driver (keys %chips_detected) {
|
|
my @optionlist = ();
|
|
foreach $detection (@{$chips_detected{$driver}}) {
|
|
next unless exists $detection->{i2c_addr}
|
|
&& exists $detection->{isa_addr}
|
|
&& $i2c_adapters[$detection->{i2c_devnr}]->{used};
|
|
|
|
push @optionlist, sprintf("%d,0x%02x",
|
|
$detection->{i2c_devnr},
|
|
$detection->{i2c_addr});
|
|
}
|
|
|
|
next if not @optionlist;
|
|
$configfile = "# hwmon module options\n"
|
|
unless defined $configfile;
|
|
$configfile .= "options $driver ignore=".
|
|
(join ",", @optionlist)."\n";
|
|
}
|
|
}
|
|
|
|
# If we added any module option to handle aliases, we need to load all
|
|
# the adapter drivers so that the numbers will be the same. If not, then
|
|
# we only load the adapter drivers which are useful.
|
|
foreach $adap (@i2c_adapters) {
|
|
next unless defined $adap;
|
|
next if $adap->{autoload};
|
|
next if $adap->{driver} eq 'UNKNOWN';
|
|
next if not defined $configfile and not $adap->{used};
|
|
$bus_modules{$adap->{driver}}++;
|
|
}
|
|
|
|
# Now determine the chip probe lines
|
|
foreach $driver (keys %chips_detected) {
|
|
next if not @{$chips_detected{$driver}};
|
|
if ($driver eq "to-be-written") {
|
|
print "Note: there is no driver for ${$chips_detected{$driver}}[0]{chipname} yet.\n".
|
|
"Check https://hwmon.wiki.kernel.org/device_support_status for updates.\n\n";
|
|
} elsif (!is_module_builtin($driver)) {
|
|
open(local *INPUTFILE, "modprobe -l $driver 2>/dev/null |");
|
|
local $_;
|
|
my $modulefound = 0;
|
|
while (<INPUTFILE>) {
|
|
if (m@/@) {
|
|
$modulefound = 1;
|
|
last;
|
|
}
|
|
}
|
|
close(INPUTFILE);
|
|
# Check return value from modprobe in case modprobe -l
|
|
# isn't supported
|
|
if ((($? >> 8) == 0) && ! $modulefound) {
|
|
print "Warning: the required module $driver is not currently installed\n".
|
|
"on your system. Check https://hwmon.wiki.kernel.org/device_support_status for\n".
|
|
"driver availability.\n\n";
|
|
} else {
|
|
$hwmon_modules{$driver}++
|
|
unless hwmon_is_autoloaded($driver);
|
|
}
|
|
}
|
|
}
|
|
|
|
my @bus_modules = sort keys %bus_modules;
|
|
my @hwmon_modules = sort keys %hwmon_modules;
|
|
return ($configfile, \@bus_modules, \@hwmon_modules);
|
|
}
|
|
|
|
sub write_config
|
|
{
|
|
my ($configfile, $bus_modules, $hwmon_modules) = @_;
|
|
|
|
if (defined $configfile) {
|
|
my $have_modprobe_d = -d '/etc/modprobe.d';
|
|
printf "Do you want to \%s /etc/modprobe.d/lm_sensors.conf? (\%s): ",
|
|
(-e '/etc/modprobe.d/lm_sensors.conf' ? 'overwrite' : 'generate'),
|
|
($have_modprobe_d ? 'YES/no' : 'yes/NO');
|
|
$_ = read_answer();
|
|
if (($have_modprobe_d and not m/^\s*n/i) or m/^\s*y/i) {
|
|
unless ($have_modprobe_d) {
|
|
mkdir('/etc/modprobe.d', 0777)
|
|
or die "Sorry, can't create /etc/modprobe.d ($!)";
|
|
}
|
|
open(local *MODPROBE_D, ">/etc/modprobe.d/lm_sensors.conf")
|
|
or die "Sorry, can't create /etc/modprobe.d/lm_sensors.conf ($!)";
|
|
print MODPROBE_D "# Generated by sensors-detect on " . scalar localtime() . "\n";
|
|
print MODPROBE_D $configfile;
|
|
close(MODPROBE_D);
|
|
} else {
|
|
print "To make the sensors modules behave correctly, add these lines to\n".
|
|
"/etc/modprobe.conf:\n\n";
|
|
print "#----cut here----\n".
|
|
$configfile.
|
|
"#----cut here----\n\n";
|
|
}
|
|
}
|
|
|
|
my $have_sysconfig = -d '/etc/sysconfig';
|
|
printf "Do you want to \%s /etc/sysconfig/lm_sensors? (\%s): ",
|
|
(-e '/etc/sysconfig/lm_sensors' ? 'overwrite' : 'generate'),
|
|
($have_sysconfig ? 'YES/no' : 'yes/NO');
|
|
$_ = read_answer();
|
|
if (($have_sysconfig and not m/^\s*n/i) or m/^\s*y/i) {
|
|
unless ($have_sysconfig) {
|
|
mkdir('/etc/sysconfig', 0777)
|
|
or die "Sorry, can't create /etc/sysconfig ($!)";
|
|
}
|
|
open(local *SYSCONFIG, ">/etc/sysconfig/lm_sensors")
|
|
or die "Sorry, can't create /etc/sysconfig/lm_sensors ($!)";
|
|
print SYSCONFIG "# Generated by sensors-detect on " . scalar localtime() . "\n";
|
|
print SYSCONFIG <<'EOT';
|
|
# This file is sourced by /etc/init.d/lm_sensors and defines the modules to
|
|
# be loaded/unloaded.
|
|
#
|
|
# The format of this file is a shell script that simply defines variables:
|
|
# HWMON_MODULES for hardware monitoring driver modules, and optionally
|
|
# BUS_MODULES for any required bus driver module (for example for I2C or SPI).
|
|
|
|
EOT
|
|
print SYSCONFIG "BUS_MODULES=\"", join(" ", @{$bus_modules}), "\"\n"
|
|
if @{$bus_modules};
|
|
print SYSCONFIG "HWMON_MODULES=\"", join(" ", @{$hwmon_modules}), "\"\n";
|
|
close(SYSCONFIG);
|
|
|
|
if ($systemd_systemctl && $systemd_system_dir) {
|
|
if (-f "$systemd_system_dir/lm_sensors.service") {
|
|
system($systemd_systemctl, "enable", "lm_sensors.service");
|
|
system($systemd_systemctl, "start", "lm_sensors.service");
|
|
# All done, don't check for /etc/init.d/lm_sensors
|
|
} else {
|
|
print "Copy prog/init/lm_sensors.service to $systemd_system_dir\n".
|
|
"and run 'systemctl enable lm_sensors.service'\n".
|
|
"for initialization at boot time.\n";
|
|
}
|
|
return;
|
|
}
|
|
|
|
print "Copy prog/init/lm_sensors.init to /etc/init.d/lm_sensors\n".
|
|
"for initialization at boot time.\n"
|
|
unless -f "/etc/init.d/lm_sensors";
|
|
|
|
if (-x "/sbin/insserv" && -f "/etc/init.d/lm_sensors") {
|
|
system("/sbin/insserv", "/etc/init.d/lm_sensors");
|
|
} elsif (-x "/sbin/chkconfig" && -f "/etc/init.d/lm_sensors") {
|
|
system("/sbin/chkconfig", "lm_sensors", "on");
|
|
if (-x "/sbin/service") {
|
|
system("/sbin/service", "lm_sensors", "start");
|
|
}
|
|
} else {
|
|
print "You should now start the lm_sensors service to load the required\n".
|
|
"kernel modules.\n\n";
|
|
}
|
|
} else {
|
|
print "To load everything that is needed, add this to one of the system\n".
|
|
"initialization scripts (e.g. /etc/rc.d/rc.local):\n\n";
|
|
print "#----cut here----\n";
|
|
if (@{$bus_modules}) {
|
|
print "# Adapter drivers\n";
|
|
print "modprobe $_\n" foreach (@{$bus_modules});
|
|
}
|
|
print "# Chip drivers\n";
|
|
print "modprobe $_\n" foreach (@{$hwmon_modules});
|
|
print((-e '/usr/bin/sensors' ?
|
|
"/usr/bin/sensors -s\n" :
|
|
"/usr/local/bin/sensors -s\n").
|
|
"#----cut here----\n\n");
|
|
|
|
print "You really should try these commands right now to make sure everything\n".
|
|
"is working properly. Monitoring programs won't work until the needed\n".
|
|
"modules are loaded.\n\n";
|
|
}
|
|
}
|
|
|
|
sub main
|
|
{
|
|
my ($input, $superio_features);
|
|
|
|
# Parse command line options
|
|
while (defined $ARGV[0]) {
|
|
if ($ARGV[0] eq "--stat") {
|
|
$opt{stat} = 1;
|
|
} elsif ($ARGV[0] eq "--auto") {
|
|
$opt{auto} = 1;
|
|
} else {
|
|
print STDERR "Error: unknown option $ARGV[0]\n";
|
|
exit 1;
|
|
}
|
|
shift @ARGV;
|
|
}
|
|
|
|
if ($opt{stat}) {
|
|
show_i2c_stats();
|
|
exit 0;
|
|
}
|
|
|
|
# We won't go very far if not root
|
|
unless ($> == 0) {
|
|
print "You need to be root to run this script.\n";
|
|
exit -1;
|
|
}
|
|
|
|
initialize_kernel_version();
|
|
initialize_conf();
|
|
initialize_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();
|
|
|
|
if ($systemd_systemctl && $systemd_system_dir &&
|
|
-f "$systemd_system_dir/lm_sensors.service") {
|
|
system("$systemd_systemctl", "stop", "lm_sensors.service");
|
|
} elsif (-x "/sbin/service" && -f "/etc/init.d/lm_sensors" &&
|
|
-f "/var/lock/subsys/lm_sensors") {
|
|
system("/sbin/service", "lm_sensors", "stop");
|
|
}
|
|
|
|
print "# sensors-detect version ".LM_VERSION."\n";
|
|
initialize_dmi_data();
|
|
print_dmi_summary();
|
|
print_kernel_version();
|
|
print_cpu_info();
|
|
print "\n";
|
|
|
|
if ($opt{auto}) {
|
|
print "Running in automatic mode, default answers to all questions\n",
|
|
"are assumed.\n\n";
|
|
} else {
|
|
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\n";
|
|
}
|
|
|
|
print "Some south bridges, CPUs or memory controllers contain embedded sensors.\n".
|
|
"Do you want to scan for them? This is totally safe. (YES/no): ";
|
|
$input = read_answer();
|
|
unless ($input =~ /^\s*n/i) {
|
|
# Load the cpuid driver if needed
|
|
unless (-e "$sysfs_root/class/cpuid") {
|
|
load_module("cpuid");
|
|
udev_settle();
|
|
}
|
|
|
|
$| = 1;
|
|
foreach my $entry (@cpu_ids) {
|
|
scan_cpu($entry);
|
|
}
|
|
$| = 0;
|
|
}
|
|
print "\n";
|
|
|
|
$superio_features = 0;
|
|
# Skip "random" I/O port probing on non-x86 machines
|
|
if ($kernel_arch =~ m/^i[3456]86$/
|
|
|| $kernel_arch eq 'x86_64') {
|
|
print "Some Super I/O chips contain embedded 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): ";
|
|
$input = read_answer();
|
|
unless ($input =~ /^\s*n/i) {
|
|
if (initialize_ioports()) {
|
|
$superio_features |= scan_superio(0x2e, 0x2f);
|
|
$superio_features |= scan_superio(0x4e, 0x4f);
|
|
close_ioports();
|
|
}
|
|
}
|
|
print "\n";
|
|
|
|
unless (is_laptop()) {
|
|
print "Some systems (mainly servers) implement IPMI, a set of common interfaces\n".
|
|
"through which system health data may be retrieved, amongst other things.\n".
|
|
"We first try to get the information from SMBIOS. If we don't find it\n".
|
|
"there, we have to read from arbitrary I/O ports to probe for such\n".
|
|
"interfaces. This is normally safe. Do you want to scan for IPMI\n".
|
|
"interfaces? (YES/no): ";
|
|
$input = read_answer();
|
|
unless ($input =~ /^\s*n/i) {
|
|
if (!ipmi_from_smbios()) {
|
|
if (initialize_ioports()) {
|
|
scan_isa_bus(\@ipmi_ifs);
|
|
close_ioports();
|
|
}
|
|
}
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
printf "Some hardware monitoring chips are accessible through the ISA I/O ports.\n".
|
|
"We have to write to arbitrary I/O ports to probe them. This is usually\n".
|
|
"safe though. Yes, you do have ISA I/O ports even if you do not have any\n".
|
|
"ISA slots! Do you want to scan the ISA I/O ports? (\%s): ",
|
|
$superio_features ? "yes/NO" : "YES/no";
|
|
$input = read_answer();
|
|
unless ($input =~ /^\s*n/i
|
|
|| ($superio_features && $input !~ /^\s*y/i)) {
|
|
if (initialize_ioports()) {
|
|
scan_isa_bus(\@chip_ids);
|
|
close_ioports();
|
|
}
|
|
}
|
|
print "\n";
|
|
}
|
|
|
|
print "Lastly, we can probe the I2C/SMBus adapters for connected hardware\n".
|
|
"monitoring devices. This is the most risky part, and while it works\n".
|
|
"reasonably well on most systems, it has been reported to cause trouble\n".
|
|
"on some systems.\n".
|
|
"Do you want to probe the I2C/SMBus adapters now? (YES/no): ";
|
|
|
|
$input = read_answer();
|
|
unless ($input =~ /^\s*n/i) {
|
|
adapter_pci_detection();
|
|
load_module("i2c-dev") unless -e "$sysfs_root/class/i2c-dev";
|
|
initialize_i2c_adapters_list();
|
|
$i2c_addresses_to_scan = i2c_addresses_to_scan();
|
|
print "\n";
|
|
|
|
# Skip SMBus probing by default if Super-I/O has all the features
|
|
my $by_default = ~$superio_features & (FEAT_IN | FEAT_FAN | FEAT_TEMP);
|
|
# Except on Asus and Tyan boards which often have more than
|
|
# one hardware monitoring chip
|
|
$by_default = 1 if dmi_match('board_vendor', 'asustek', 'tyan',
|
|
'supermicro');
|
|
|
|
udev_settle();
|
|
for (my $dev_nr = 0; $dev_nr < @i2c_adapters; $dev_nr++) {
|
|
next unless exists $i2c_adapters[$dev_nr];
|
|
scan_i2c_adapter($dev_nr, $by_default);
|
|
}
|
|
filter_out_fake_i2c_drivers();
|
|
}
|
|
|
|
if (!keys %chips_detected) {
|
|
print "Sorry, no sensors were detected.\n";
|
|
if (is_laptop() && -d "$sysfs_root/firmware/acpi") {
|
|
print "This is relatively common on laptops, where thermal management is\n".
|
|
"handled by ACPI rather than the OS.\n";
|
|
} else {
|
|
print "Either your system has no sensors, or they are not supported, or\n".
|
|
"they are connected to an I2C or SMBus adapter that is not\n".
|
|
"supported. If you find out what chips are on your board, check\n".
|
|
"https://hwmon.wiki.kernel.org/device_support_status for driver status.\n";
|
|
}
|
|
exit;
|
|
}
|
|
|
|
print "\nNow follows a summary of the probes I have just done.\n";
|
|
unless ($opt{auto}) {
|
|
print "Just press ENTER to continue: ";
|
|
<STDIN>;
|
|
}
|
|
|
|
initialize_hwmon_autoloaded();
|
|
foreach my $driver (keys %chips_detected) {
|
|
next unless @{$chips_detected{$driver}};
|
|
find_aliases($chips_detected{$driver});
|
|
print "\nDriver `$driver'";
|
|
print " (autoloaded)" if hwmon_is_autoloaded($driver);
|
|
print " (built-in)" if is_module_builtin($driver);
|
|
print ":\n";
|
|
print_chips_report($chips_detected{$driver});
|
|
}
|
|
print "\n";
|
|
|
|
my ($configfile, $bus_modules, $hwmon_modules) = generate_modprobes();
|
|
|
|
if (@{$hwmon_modules}) {
|
|
write_config($configfile, $bus_modules, $hwmon_modules);
|
|
} else {
|
|
print "No modules to load, skipping modules configuration.\n\n";
|
|
}
|
|
|
|
unload_modules();
|
|
|
|
# Check if running non-interactively without --auto
|
|
if (!$opt{auto} && ! -t STDIN) {
|
|
print "***************************************************************\n".
|
|
"Warning: the preferred way to run this script non-interactively\n".
|
|
"is with option --auto. Other methods are discouraged and may\n".
|
|
"stop working at some point in the future.\n".
|
|
"***************************************************************\n\n";
|
|
}
|
|
}
|
|
|
|
sub cleanup_on_int
|
|
{
|
|
print "\n";
|
|
unload_modules();
|
|
exit;
|
|
}
|
|
|
|
$SIG{INT} = \&cleanup_on_int;
|
|
|
|
main;
|