mirror of
https://github.com/lm-sensors/lm-sensors
synced 2025-08-27 04:19:15 +00:00
git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@2596 7894878c-1315-0410-8ee3-d5d059ff63e0
4915 lines
149 KiB
Perl
Executable File
4915 lines
149 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
|
|
#
|
|
# sensors-detect - Detect PCI bus and chips
|
|
# Copyright (C) 1998 - 2002 Frodo Looijaard <frodol@dds.nl>
|
|
# Copyright (C) 2000 - 2004 The lm_sensors team
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
#
|
|
|
|
# TODO: Better handling of chips with several addresses
|
|
|
|
# A Perl wizard really ought to look upon this; the PCI and I2C stuff should
|
|
# each be put in a separate file, using modules and packages. That is beyond
|
|
# me.
|
|
|
|
require 5.004;
|
|
|
|
use strict;
|
|
use Fcntl;
|
|
use POSIX;
|
|
|
|
# Just in case a root user doesn't have /sbin in his/her path for some reason
|
|
# (was seen once)
|
|
$ENV{PATH} = '/sbin:'.$ENV{PATH}
|
|
unless $ENV{PATH} =~ m,(^|:)/sbin/?(:|$),;
|
|
# Same for /usr/local/sbin since we need i2cdetect which is installed there
|
|
# by default (reported by Lennard Klein)
|
|
$ENV{PATH} = '/usr/local/sbin:'.$ENV{PATH}
|
|
unless $ENV{PATH} =~ m,(^|:)/usr/local/sbin/?(:|$),;
|
|
|
|
#########################
|
|
# CONSTANT DECLARATIONS #
|
|
#########################
|
|
|
|
use vars qw(@pci_adapters @chip_ids @superio_ids);
|
|
|
|
# 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), func (PCI
|
|
# Function) and procid (string as appears in /proc/pci; see linux/driver/pci,
|
|
# either pci.c or oldproc.c). If no driver is written yet, set the
|
|
# driver (Driver Name) field to "to-be-written".
|
|
# The match (Match Description) field should
|
|
# contain a function which returns zero if its two parameter matches
|
|
# the text as it would appear in /proc/bus/i2c.
|
|
@pci_adapters = (
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x7113,
|
|
func => 3,
|
|
procid => "Intel 82371AB PIIX4 ACPI",
|
|
driver => "i2c-piix4",
|
|
match => sub { $_[0] =~ /^SMBus PIIX4 adapter at / },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x719b,
|
|
func => 3,
|
|
procid => "Intel 82443MX Mobile",
|
|
driver => "i2c-piix4",
|
|
match => sub { $_[0] =~ /^SMBus PIIX4 adapter at / },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x2413,
|
|
func => 3,
|
|
procid => "Intel 82801AA ICH",
|
|
driver => "i2c-i801",
|
|
match => sub { $_[0] =~ /^SMBus I801 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x2423,
|
|
func => 3,
|
|
procid => "Intel 82801AB ICH0",
|
|
driver => "i2c-i801",
|
|
match => sub { $_[0] =~ /^SMBus I801 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x2443,
|
|
func => 3,
|
|
procid => "Intel 82801BA ICH2",
|
|
driver => "i2c-i801",
|
|
match => sub { $_[0] =~ /^SMBus I801 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x2483,
|
|
func => 3,
|
|
procid => "Intel 82801CA/CAM ICH3",
|
|
driver => "i2c-i801",
|
|
match => sub { $_[0] =~ /^SMBus I801 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x24C3,
|
|
func => 3,
|
|
procid => "Intel 82801DB ICH4",
|
|
driver => "i2c-i801",
|
|
match => sub { $_[0] =~ /^SMBus I801 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x24D3,
|
|
func => 3,
|
|
procid => "Intel 82801EB ICH5",
|
|
driver => "i2c-i801",
|
|
match => sub { $_[0] =~ /^SMBus I801 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x25A4,
|
|
func => 3,
|
|
procid => "Intel 6300ESB",
|
|
driver => "i2c-i801",
|
|
match => sub { $_[0] =~ /^SMBus I801 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x266A,
|
|
func => 3,
|
|
procid => "Intel ICH6",
|
|
driver => "i2c-i801",
|
|
match => sub { $_[0] =~ /^SMBus I801 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x3040,
|
|
func => 3,
|
|
procid => "VIA Technologies VT82C586B Apollo ACPI",
|
|
driver => "i2c-via",
|
|
match => sub { $_[0] =~ /^VIA i2c/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x3050,
|
|
func => 3,
|
|
procid => "VIA Technologies VT82C596 Apollo ACPI",
|
|
driver => "i2c-viapro",
|
|
match => sub { $_[0] =~ /^SMBus Via Pro adapter at/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x3051,
|
|
func => 3,
|
|
procid => "VIA Technologies VT82C596B ACPI",
|
|
driver => "i2c-viapro",
|
|
match => sub { $_[0] =~ /^SMBus Via Pro adapter at/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x3057,
|
|
func => 4,
|
|
procid => "VIA Technologies VT82C686 Apollo ACPI",
|
|
driver => "i2c-viapro",
|
|
match => sub { $_[0] =~ /^SMBus Via Pro adapter at/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x3074,
|
|
func => 0,
|
|
procid => "VIA Technologies VT8233 VLink South Bridge",
|
|
driver => "i2c-viapro",
|
|
match => sub { $_[0] =~ /^SMBus Via Pro adapter at/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x3147,
|
|
func => 0,
|
|
procid => "VIA Technologies VT8233A South Bridge",
|
|
driver => "i2c-viapro",
|
|
match => sub { $_[0] =~ /^SMBus Via Pro adapter at/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x3177,
|
|
func => 0,
|
|
procid => "VIA Technologies VT8233A/8235 South Bridge",
|
|
driver => "i2c-viapro",
|
|
match => sub { $_[0] =~ /^SMBus Via Pro adapter at/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x3227,
|
|
func => 0,
|
|
procid => "VIA Technologies VT8237 South Bridge",
|
|
driver => "i2c-viapro",
|
|
match => sub { $_[0] =~ /^SMBus Via Pro adapter at/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1106,
|
|
devid => 0x8235,
|
|
func => 4,
|
|
procid => "VIA Technologies VT8231 South Bridge",
|
|
driver => "i2c-viapro",
|
|
match => sub { $_[0] =~ /^SMBus Via Pro adapter at/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x5597,
|
|
func => 0,
|
|
procid => "Silicon Integrated Systems SIS5581/5582/5597/5598 (To be written - Do not use 5595 drivers)",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x5598,
|
|
func => 0,
|
|
procid => "Silicon Integrated Systems SIS5598 (To be written - Do not use 5595 drivers)",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x0540,
|
|
func => 0,
|
|
procid => "Silicon Integrated Systems SIS540 (To be written - Do not use 5595 drivers)",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x0630,
|
|
func => 0,
|
|
procid => "Silicon Integrated Systems SIS630",
|
|
driver => "i2c-sis630",
|
|
match => sub { $_[0] =~ /^SMBus SIS630 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x0730,
|
|
func => 0,
|
|
procid => "Silicon Integrated Systems SIS730",
|
|
driver => "i2c-sis630",
|
|
match => sub { $_[0] =~ /^SMBus SIS630 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
#
|
|
# Both Ali chips below have same PCI ID. Can't be helped. Only one should load.
|
|
#
|
|
{
|
|
vendid => 0x10b9,
|
|
devid => 0x7101,
|
|
func => 0,
|
|
procid => "Acer Labs 1533/1543",
|
|
driver => "i2c-ali15x3",
|
|
match => sub { $_[0] =~ /^SMBus ALI15X3 adapter at/ },
|
|
},
|
|
{
|
|
vendid => 0x10b9,
|
|
devid => 0x7101,
|
|
func => 0,
|
|
procid => "Acer Labs 1535",
|
|
driver => "i2c-ali1535",
|
|
match => sub { $_[0] =~ /^SMBus ALI1535 adapter at/ },
|
|
},
|
|
{
|
|
vendid => 0x10b9,
|
|
devid => 0x1563,
|
|
func => 0,
|
|
procid => "Acer Labs 1563",
|
|
driver => "i2c-ali1563",
|
|
match => sub { $_[0] =~ /^SMBus ALi 1563 Adapter @/ },
|
|
},
|
|
{
|
|
vendid => 0x106b,
|
|
devid => 0x000e,
|
|
func => 0,
|
|
procid => "Apple Computer Inc. Hydra Mac I/O",
|
|
driver => "i2c-hydra",
|
|
match => sub { $_[0] =~ /^Hydra i2c/ },
|
|
},
|
|
{
|
|
vendid => 0x1022,
|
|
devid => 0x740b,
|
|
func => 3,
|
|
procid => "AMD-756 Athlon ACPI",
|
|
driver => "i2c-amd756",
|
|
match => sub { $_[0] =~ /^SMBus AMD756 adapter at [0-9,a-f]{4}/ },
|
|
},
|
|
{
|
|
vendid => 0x1022,
|
|
devid => 0x7413,
|
|
func => 3,
|
|
procid => "AMD-766 Athlon ACPI",
|
|
driver => "i2c-amd756",
|
|
match => sub { $_[0] =~ /^SMBus AMD766 adapter at [0-9,a-f]{4}/ },
|
|
},
|
|
{
|
|
vendid => 0x1022,
|
|
devid => 0x7443,
|
|
func => 3,
|
|
procid => "AMD-768 System Management",
|
|
driver => "i2c-amd756",
|
|
match => sub { $_[0] =~ /^SMBus AMD768 adapter at [0-9,a-f]{4}/ },
|
|
},
|
|
{
|
|
vendid => 0x1022,
|
|
devid => 0x746b,
|
|
func => 3,
|
|
procid => "AMD-8111 ACPI",
|
|
driver => "i2c-amd756",
|
|
match => sub { $_[0] =~ /^SMBus AMD8111 adapter at [0-9,a-f]{4}/ },
|
|
},
|
|
{
|
|
vendid => 0x1022,
|
|
devid => 0x746a,
|
|
func => 2,
|
|
procid => "AMD-8111 SMBus 2.0",
|
|
driver => "i2c-amd8111",
|
|
match => sub { $_[0] =~ /^SMBus2 AMD8111 adapter at [0-9,a-f]{4}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x0519,
|
|
func => 0,
|
|
procid => "MGA 2064W [Millennium]",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x051a,
|
|
func => 0,
|
|
procid => "MGA 1064SG [Mystique]",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x051b,
|
|
func => 0,
|
|
procid => "MGA 2164W [Millennium II]",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x051e,
|
|
func => 0,
|
|
procid => "MGA 1064SG [Mystique] AGP",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x051f,
|
|
func => 0,
|
|
procid => "MGA 2164W [Millennium II] AGP",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x1000,
|
|
func => 0,
|
|
procid => "MGA G100 [Productiva]",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x1001,
|
|
func => 0,
|
|
procid => "MGA G100 [Productiva] AGP",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x0520,
|
|
func => 0,
|
|
procid => "MGA G200",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x0521,
|
|
func => 0,
|
|
procid => "MGA G200 AGP",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^DDC:fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x102b,
|
|
devid => 0x0525,
|
|
func => 0,
|
|
procid => "MGA G400 AGP",
|
|
driver => "i2c-matroxfb",
|
|
match => sub { $_[0] =~ /^(DDC,MAVEN):fb[0-9]{1,2}/ },
|
|
},
|
|
{
|
|
vendid => 0x121a,
|
|
devid => 0x0005,
|
|
func => 0,
|
|
procid => "3Dfx Voodoo3",
|
|
driver => "i2c-voodoo3",
|
|
match => sub { $_[0] =~ /Banshee adapter/ },
|
|
},
|
|
{
|
|
vendid => 0x121a,
|
|
devid => 0x0003,
|
|
func => 0,
|
|
procid => "3Dfx Voodoo Banshee",
|
|
driver => "i2c-voodoo3",
|
|
match => sub { $_[0] =~ /Banshee adapter/ },
|
|
},
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x7121,
|
|
func => 0,
|
|
procid => "Intel 82810 GMCH",
|
|
driver => "i2c-i810",
|
|
match => sub { $_[0] =~ /^I810/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x7123,
|
|
func => 0,
|
|
procid => "Intel 82810-DC100 GMCH",
|
|
driver => "i2c-i810",
|
|
match => sub { $_[0] =~ /^I810/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x7125,
|
|
func => 0,
|
|
procid => "Intel 82810E GMCH",
|
|
driver => "i2c-i810",
|
|
match => sub { $_[0] =~ /^I810/ },
|
|
} ,
|
|
{
|
|
vendid => 0x8086,
|
|
devid => 0x1132,
|
|
func => 0,
|
|
procid => "Intel 82815 GMCH",
|
|
driver => "i2c-i810",
|
|
match => sub { $_[0] =~ /^I810/ },
|
|
} ,
|
|
{
|
|
vendid => 0x12d2,
|
|
devid => 0x0018,
|
|
func => 0,
|
|
procid => "RIVA 128",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0020,
|
|
func => 0,
|
|
procid => "RIVA TNT",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0028,
|
|
func => 0,
|
|
procid => "RIVA TNT2",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0029,
|
|
func => 0,
|
|
procid => "RIVA UTNT2",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x002c,
|
|
func => 0,
|
|
procid => "RIVA VTNT2",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x002d,
|
|
func => 0,
|
|
procid => "RIVA UVTNT2",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x00a0,
|
|
func => 0,
|
|
procid => "RIVA ITNT2",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0100,
|
|
func => 0,
|
|
procid => "GeForce SDR",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0101,
|
|
func => 0,
|
|
procid => "GeForce DDR",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0102,
|
|
func => 0,
|
|
procid => "Quadro",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0150,
|
|
func => 0,
|
|
procid => "GeForce2 GTS",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0110,
|
|
func => 0,
|
|
procid => "GeForce2 MX",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0111,
|
|
func => 0,
|
|
procid => "GeForce2 MX2",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0113,
|
|
func => 0,
|
|
procid => "Quadro2 MXR",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0151,
|
|
func => 0,
|
|
procid => "GeForce2 GTS2",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0152,
|
|
func => 0,
|
|
procid => "GeForce2 Ultra",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0153,
|
|
func => 0,
|
|
procid => "Quadro2 Pro",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0312,
|
|
func => 0,
|
|
procid => "GeForce FX 5600",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
},
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0330,
|
|
func => 0,
|
|
procid => "GeForce FX 5900 Ultra",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
},
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0331,
|
|
func => 0,
|
|
procid => "GeForce FX 5900",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
},
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0332,
|
|
func => 0,
|
|
procid => "GeForce FX 5900 XT",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
},
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0333,
|
|
func => 0,
|
|
procid => "GeForce FX 5950 Ultra",
|
|
driver => "rivatv",
|
|
match => sub { $_[0] =~ /^NVIDIA display/ },
|
|
},
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x01b4,
|
|
func => 1,
|
|
procid => "nVidia nForce SMBus",
|
|
driver => "i2c-amd756",
|
|
match => sub { $_[0] =~ /^SMBus nVidia nForce adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x10de,
|
|
devid => 0x0064,
|
|
func => 1,
|
|
procid => "nVidia Corporation nForce2 SMBus (MCP)",
|
|
driver => "i2c-nforce2",
|
|
match => sub { $_[0] =~ /^SMBus nForce2 adapter at / },
|
|
} ,
|
|
{
|
|
vendid => 0x1166,
|
|
devid => 0x0200,
|
|
func => 0,
|
|
procid => "ServerWorks OSB4 South Bridge",
|
|
driver => "i2c-piix4",
|
|
match => sub { $_[0] =~ /^SMBus PIIX4 adapter at / },
|
|
} ,
|
|
{
|
|
vendid => 0x1055,
|
|
devid => 0x9463,
|
|
func => 0,
|
|
procid => "SMSC Victory66 South Bridge",
|
|
driver => "i2c-piix4",
|
|
match => sub { $_[0] =~ /^SMBus PIIX4 adapter at / },
|
|
} ,
|
|
{
|
|
vendid => 0x1166,
|
|
devid => 0x0201,
|
|
func => 0,
|
|
procid => "ServerWorks CSB5 South Bridge",
|
|
driver => "i2c-piix4",
|
|
match => sub { $_[0] =~ /^SMBus PIIX4 adapter at / },
|
|
} ,
|
|
{
|
|
vendid => 0x1166,
|
|
devid => 0x0203,
|
|
func => 0,
|
|
procid => "ServerWorks CSB6 South Bridge",
|
|
driver => "i2c-piix4",
|
|
match => sub { $_[0] =~ /^SMBus PIIX4 adapter at / },
|
|
} ,
|
|
{
|
|
vendid => 0x1283,
|
|
devid => 0x8172,
|
|
func => 0,
|
|
procid => "ITE 8172G MIPS/SH4 Support Chip",
|
|
driver => "i2c-adap-ite",
|
|
match => sub { $_[0] =~ /^ITE IIC adapter/ },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8A20,
|
|
func => 0,
|
|
procid => "S3 Savage 3D",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8A21,
|
|
func => 0,
|
|
procid => "S3 Savage 3D MV",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8A22,
|
|
func => 0,
|
|
procid => "S3 Savage 4",
|
|
driver => "i2c-savage4",
|
|
match => sub { $_[0] =~ /Savage4 adapter/ },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x9102,
|
|
func => 0,
|
|
procid => "S3 Savage 2000",
|
|
driver => "i2c-savage4",
|
|
match => sub { $_[0] =~ /Savage4 adapter/ },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8A25,
|
|
func => 0,
|
|
procid => "S3 ProSavage PM",
|
|
driver => "to-be-written",
|
|
match => sub { $_[0] =~ /^dontmatchthis/ },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8A26,
|
|
func => 0,
|
|
procid => "S3 ProSavage KM",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8C10,
|
|
func => 0,
|
|
procid => "S3 Savage MX MV",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8C11,
|
|
func => 0,
|
|
procid => "S3 Savage MX",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8C12,
|
|
func => 0,
|
|
procid => "S3 Savage IX MV",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
{
|
|
vendid => 0x5333,
|
|
devid => 0x8C13,
|
|
func => 0,
|
|
procid => "S3 Savage IX",
|
|
driver => "to-be-written",
|
|
match => sub { 0 },
|
|
} ,
|
|
);
|
|
|
|
# The following entries used to appear directly in @pci_adapters.
|
|
# Because of the tendency of SiS chipsets to have their real PCI
|
|
# IDs obscured, we have to qualify these with a custom detection
|
|
# routine before we add them to the @pci_adapters list.
|
|
#
|
|
use vars qw(@pci_adapters_sis5595 @pci_adapters_sis645 @pci_adapters_sis96x);
|
|
@pci_adapters_sis5595 = (
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x0008,
|
|
func => 0,
|
|
procid => "Silicon Integrated Systems SIS5595",
|
|
driver => "i2c-sis5595",
|
|
match => sub { $_[0] =~ /^SMBus SIS5595 adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
);
|
|
|
|
@pci_adapters_sis645 = (
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x0008,
|
|
func => 0,
|
|
procid => "Silicon Integrated Systems SIS5595",
|
|
driver => "i2c-sis645",
|
|
match => sub { $_[0] =~ /^SiS645 SMBus adapter at [0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x0016,
|
|
func => 1,
|
|
procid => "Silicon Integrated Systems SMBus Controller",
|
|
driver => "i2c-sis645",
|
|
match => sub { $_[0] =~ /^SiS645 SMBus adapter at 0x[0-9,a-f]{4}/ },
|
|
} ,
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x0018,
|
|
func => 0,
|
|
procid => "Silicon Integrated Systems 85C503/5513 (LPC Bridge)",
|
|
driver => "i2c-sis645",
|
|
match => sub { $_[0] =~ /^SiS645 SMBus adapter at 0x[0-9,a-f]{4}/ },
|
|
} ,
|
|
);
|
|
|
|
@pci_adapters_sis96x = (
|
|
{
|
|
vendid => 0x1039,
|
|
devid => 0x0016,
|
|
func => 1,
|
|
procid => "Silicon Integrated Systems SMBus Controller",
|
|
driver => "i2c-sis96x",
|
|
match => sub { $_[0] =~ /^SiS96x SMBus adapter at 0x[0-9,a-f]{4}/ },
|
|
} ,
|
|
);
|
|
|
|
use subs qw(mtp008_detect lm78_detect lm78_isa_detect lm78_alias_detect
|
|
lm75_detect lm80_detect w83781d_detect w83781d_alias_detect
|
|
adm1025_detect w83781d_isa_detect gl518sm_detect gl520sm_detect
|
|
adm9240_detect adm1021_detect sis5595_isa_detect eeprom_detect
|
|
via686a_isa_detect adm1022_detect gl525sm_detect
|
|
lm87_detect ite_detect ite_isa_detect ite_alias_detect
|
|
ddcmonitor_detect ds1621_detect adm1024_detect fscpos_detect
|
|
fscscy_detect arp_detect ipmi_kcs_detect
|
|
ipmi_smic_detect via8231_isa_detect lm85_detect smartbatt_detect
|
|
adm1026_detect w83l785ts_detect lm83_detect lm90_detect
|
|
saa1064_detect w83l784r_detect mozart_detect max6650_detect
|
|
fscher_detect adm1029_detect adm1031_detect max6900_detect
|
|
m5879_detect pca9540_detect smartbatt_mgr_detect
|
|
smartbatt_chgr_detect adt7467_detect lm92_detect max1619_detect
|
|
lm93_detect);
|
|
|
|
# This is a list of all recognized chips.
|
|
# Each entry must have the following fields:
|
|
# name: The full chip name
|
|
# driver: The driver name (without .o extension). Put in exactly
|
|
# "to-be-written" if it is not yet available.
|
|
# i2c_addrs (optional): For I2C chips, the range of valid I2C addresses to
|
|
# probe. Recommend avoiding 0x69 because of clock chips.
|
|
# i2c_driver_addrs (optional): For I2C chips, the range of valid I2C
|
|
# addresses probed by the kernel driver. Strictly optional.
|
|
# i2c_detect (optional): For I2C chips, the function to call to detect
|
|
# this chip. The function should take two parameters: an open file
|
|
# descriptor to access the bus, and the I2C address to probe.
|
|
# isa_addrs (optional): For ISA chips, the range of valid port addresses to
|
|
# probe.
|
|
# isa_driver_addrs (optional): For ISA chips, the range of valid ISA
|
|
# addresses probed by the kernel driver. Strictly optional.
|
|
# isa_detect (optional): For ISA chips, the function to call to detect
|
|
# this chip. The function should take one parameter: the ISA address
|
|
# to probe.
|
|
# alias_detect (optional): For chips which can be both on the ISA and the
|
|
# I2C bus, a function which detectes whether two entries are the same.
|
|
# The function should take three parameters: The ISA address, the
|
|
# I2C bus number, and the I2C address.
|
|
@chip_ids = (
|
|
{
|
|
name => "Myson MTP008",
|
|
driver => "mtp008",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { mtp008_detect @_},
|
|
} ,
|
|
{
|
|
name => "National Semiconductor LM78",
|
|
driver => "lm78",
|
|
i2c_addrs => [0x20..0x2f],
|
|
i2c_detect => sub { lm78_detect 0, @_},
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { lm78_isa_detect 0, @_ },
|
|
alias_detect => sub { lm78_alias_detect 0, @_ },
|
|
} ,
|
|
{
|
|
name => "National Semiconductor LM78-J",
|
|
driver => "lm78",
|
|
i2c_addrs => [0x20..0x2f],
|
|
i2c_detect => sub { lm78_detect 1, @_ },
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { lm78_isa_detect 1, @_ },
|
|
alias_detect => sub { lm78_alias_detect 1, @_ },
|
|
} ,
|
|
{
|
|
name => "National Semiconductor LM79",
|
|
driver => "lm78",
|
|
i2c_addrs => [0x20..0x2f],
|
|
i2c_detect => sub { lm78_detect 2, @_ },
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { lm78_isa_detect 2, @_ },
|
|
alias_detect => sub { lm78_alias_detect 2, @_ },
|
|
} ,
|
|
{
|
|
name => "National Semiconductor LM75",
|
|
driver => "lm75",
|
|
i2c_addrs => [0x48..0x4f],
|
|
i2c_detect => sub { lm75_detect @_},
|
|
} ,
|
|
{
|
|
name => "National Semiconductor LM80",
|
|
driver => "lm80",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { lm80_detect @_} ,
|
|
},
|
|
{
|
|
name => "National Semiconductor LM85",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect 0x01, @_},
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1027, ADT7460 or ADT7463",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect 0x41, @_},
|
|
},
|
|
{
|
|
name => "SMSC EMC6D100 or EMC6D101",
|
|
driver => "lm85",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm85_detect 0x5c, @_},
|
|
},
|
|
{
|
|
name => "Analog Devices ADT7467",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x2e],
|
|
i2c_detect => sub { adt7467_detect 0, @_},
|
|
},
|
|
{
|
|
name => "National Semiconductor LM87",
|
|
driver => "lm87",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm87_detect @_} ,
|
|
},
|
|
{
|
|
name => "National Semiconductor LM93",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { lm93_detect @_ },
|
|
},
|
|
{
|
|
name => "Winbond W83781D",
|
|
driver => "w83781d",
|
|
i2c_detect => sub { w83781d_detect 0, @_},
|
|
i2c_addrs => [0x20..0x2f],
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { w83781d_isa_detect 0, @_ },
|
|
alias_detect => sub { w83781d_alias_detect 0, @_ },
|
|
} ,
|
|
{
|
|
name => "Winbond W83782D",
|
|
driver => "w83781d",
|
|
i2c_addrs => [0x20..0x2f],
|
|
i2c_detect => sub { w83781d_detect 1, @_},
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { w83781d_isa_detect 1, @_ },
|
|
alias_detect => sub { w83781d_alias_detect 1, @_ },
|
|
} ,
|
|
{
|
|
name => "Winbond W83783S",
|
|
driver => "w83781d",
|
|
i2c_addrs => [0x2d],
|
|
i2c_detect => sub { w83781d_detect 2, @_},
|
|
} ,
|
|
{
|
|
name => "Winbond W83791D",
|
|
driver => "w83781d",
|
|
i2c_addrs => [0x2c..0x2f],
|
|
i2c_detect => sub { w83781d_detect 7, @_},
|
|
} ,
|
|
{
|
|
name => "Winbond W83627HF",
|
|
driver => "w83781d",
|
|
i2c_addrs => [0x20..0x2f],
|
|
i2c_detect => sub { w83781d_detect 3, @_},
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { w83781d_isa_detect 3, @_ },
|
|
alias_detect => sub { w83781d_alias_detect 3, @_ },
|
|
} ,
|
|
{
|
|
name => "Asus AS99127F (rev.1)",
|
|
driver => "w83781d",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { w83781d_detect 4, @_},
|
|
} ,
|
|
{
|
|
name => "Asus AS99127F (rev.2)",
|
|
driver => "w83781d",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { w83781d_detect 5, @_},
|
|
} ,
|
|
{
|
|
name => "Asus ASB100 Bach",
|
|
driver => "asb100",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { w83781d_detect 6, @_},
|
|
} ,
|
|
{
|
|
name => "Asus ASM58 Mozart-2",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x77],
|
|
i2c_detect => sub { mozart_detect 0, @_},
|
|
} ,
|
|
{
|
|
name => "Asus AS2K129R Mozart-2",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x77],
|
|
i2c_detect => sub { mozart_detect 1, @_},
|
|
} ,
|
|
{
|
|
name => "Asus Mozart-2",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x77],
|
|
i2c_detect => sub { mozart_detect 2, @_},
|
|
} ,
|
|
{
|
|
name => "Winbond W83L784R/AR",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x2d],
|
|
i2c_detect => sub { w83l784r_detect 0, @_},
|
|
} ,
|
|
{
|
|
name => "Winbond W83L785R",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x2d],
|
|
i2c_detect => sub { w83l784r_detect 1, @_},
|
|
} ,
|
|
{
|
|
name => "Winbond W83L785TS-S",
|
|
driver => "w83l785ts",
|
|
i2c_addrs => [0x2e],
|
|
i2c_detect => sub { w83l785ts_detect 0, @_},
|
|
} ,
|
|
{
|
|
name => "Winbond W83697HF",
|
|
driver => "w83781d",
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { w83781d_isa_detect 5, @_ },
|
|
} ,
|
|
{
|
|
name => "Genesys Logic GL518SM Revision 0x00",
|
|
driver => "gl518sm",
|
|
i2c_addrs => [0x2c, 0x2d],
|
|
i2c_detect => sub { gl518sm_detect 0, @_} ,
|
|
},
|
|
{
|
|
name => "Genesys Logic GL518SM Revision 0x80",
|
|
driver => "gl518sm",
|
|
i2c_addrs => [0x2c, 0x2d],
|
|
i2c_detect => sub { gl518sm_detect 1, @_} ,
|
|
},
|
|
{
|
|
name => "Genesys Logic GL520SM",
|
|
driver => "gl520sm",
|
|
i2c_addrs => [0x2c, 0x2d],
|
|
i2c_detect => sub { gl520sm_detect @_} ,
|
|
},
|
|
{
|
|
name => "Genesys Logic GL525SM",
|
|
driver => "Unwritten (GL525SM)",
|
|
i2c_addrs => [0x2d],
|
|
i2c_detect => sub { gl525sm_detect @_} ,
|
|
},
|
|
{
|
|
name => "Analog Devices ADM9240",
|
|
driver => "adm9240",
|
|
i2c_addrs => [0x2c..0x2f],
|
|
i2c_detect => sub { adm9240_detect 0, @_ }
|
|
},
|
|
{
|
|
name => "Dallas Semiconductor DS1621",
|
|
driver => "ds1621",
|
|
i2c_addrs => [0x48..0x4f],
|
|
i2c_detect => sub { ds1621_detect @_},
|
|
} ,
|
|
{
|
|
name => "Dallas Semiconductor DS1780",
|
|
driver => "adm9240",
|
|
i2c_addrs => [0x2c..0x2f],
|
|
i2c_detect => sub { adm9240_detect 1, @_ }
|
|
},
|
|
{
|
|
name => "National Semiconductor LM81",
|
|
driver => "adm9240",
|
|
i2c_addrs => [0x2c..0x2f],
|
|
i2c_detect => sub { adm9240_detect 2, @_ }
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1026",
|
|
driver => "adm1026",
|
|
i2c_addrs => [0x2c,0x2d,0x2e],
|
|
i2c_detect => sub { adm1026_detect 0, @_ }
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1025",
|
|
driver => "adm1025",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { adm1025_detect 0, @_ }
|
|
},
|
|
{
|
|
name => "Philips NE1619",
|
|
driver => "adm1025",
|
|
i2c_addrs => [0x2c..0x2d],
|
|
i2c_detect => sub { adm1025_detect 1, @_ }
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1024",
|
|
driver => "adm1024",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { adm1024_detect 0, @_ }
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1021",
|
|
driver => "adm1021",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { adm1021_detect 0, @_ },
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1021A/ADM1023",
|
|
driver => "adm1021",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { adm1021_detect 1, @_ },
|
|
},
|
|
{
|
|
name => "Maxim MAX1617",
|
|
driver => "adm1021",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { adm1021_detect 2, @_ },
|
|
},
|
|
{
|
|
name => "Maxim MAX1617A",
|
|
driver => "adm1021",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { adm1021_detect 3, @_ },
|
|
},
|
|
{
|
|
name => "Maxim MAX6650/MAX6651",
|
|
driver => "max6650",
|
|
i2c_addrs => [0x1b,0x1f,0x48,0x4b],
|
|
i2c_detect => sub { max6650_detect 0, @_ },
|
|
},
|
|
{
|
|
name => "TI THMC10",
|
|
driver => "adm1021",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { adm1021_detect 4, @_ },
|
|
},
|
|
{
|
|
name => "National Semiconductor LM84",
|
|
driver => "adm1021",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { adm1021_detect 5, @_ },
|
|
},
|
|
{
|
|
name => "Genesys Logic GL523SM",
|
|
driver => "adm1021",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { adm1021_detect 6, @_ },
|
|
},
|
|
{
|
|
name => "Onsemi MC1066",
|
|
driver => "adm1021",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { adm1021_detect 7, @_ },
|
|
},
|
|
{
|
|
name => "Maxim MAX1619",
|
|
driver => "max1619",
|
|
i2c_addrs => [0x18..0x1a, 0x29..0x2b, 0x4c..0x4e],
|
|
i2c_detect => sub { max1619_detect 0, @_ },
|
|
},
|
|
{
|
|
name => "National Semiconductor LM82",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { lm83_detect 1, @_ },
|
|
},
|
|
{
|
|
name => "National Semiconductor LM83",
|
|
driver => "lm83",
|
|
i2c_addrs => [0x18..0x1a,0x29..0x2b,0x4c..0x4e],
|
|
i2c_detect => sub { lm83_detect 0, @_ },
|
|
},
|
|
{
|
|
name => "National Semiconductor LM90",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm90_detect 0, @_ },
|
|
},
|
|
{
|
|
name => "National Semiconductor LM89/LM99",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c..0x4d],
|
|
i2c_detect => sub { lm90_detect 1, @_ },
|
|
},
|
|
{
|
|
name => "National Semiconductor LM86",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm90_detect 2, @_ },
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1032",
|
|
driver => "lm90",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm90_detect 3, @_ },
|
|
},
|
|
{
|
|
name => "Maxim MAX6657/MAX6658/MAX6659",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm90_detect 4, @_ },
|
|
},
|
|
{
|
|
name => "Maxim MAX6659",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x4d..0x4e], # 0x4c is handled above
|
|
i2c_detect => sub { lm90_detect 4, @_ },
|
|
},
|
|
{
|
|
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 => [0x40..0x4f],
|
|
i2c_detect => sub { lm92_detect 2, @_ },
|
|
},
|
|
{
|
|
name => "Analog Devices ADT7461",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x4c],
|
|
i2c_detect => sub { lm90_detect 5, @_ },
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1029",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x28..0x2f],
|
|
i2c_detect => sub { adm1029_detect 0, @_ },
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1030",
|
|
driver => "adm1031",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { adm1031_detect 0, @_ },
|
|
},
|
|
{
|
|
name => "Analog Devices ADM1031",
|
|
driver => "adm1031",
|
|
i2c_addrs => [0x2c..0x2e],
|
|
i2c_detect => sub { adm1031_detect 1, @_ },
|
|
},
|
|
{
|
|
name => "Analog Devices 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 => "Silicon Integrated Systems SIS5595",
|
|
driver => "sis5595",
|
|
isa_addrs => [ 0 ],
|
|
isa_detect => sub { sis5595_isa_detect @_ },
|
|
},
|
|
{
|
|
name => "VIA Technologies VT82C686 Integrated Sensors",
|
|
driver => "via686a",
|
|
isa_addrs => [ 0 ],
|
|
isa_detect => sub { via686a_isa_detect @_ },
|
|
},
|
|
{
|
|
name => "VIA Technologies VT8231 Integrated Sensors",
|
|
driver => "vt8231",
|
|
isa_addrs => [ 0 ],
|
|
isa_detect => sub { via8231_isa_detect @_ },
|
|
},
|
|
{
|
|
name => "ITE IT8705F / IT8712F / SiS 950",
|
|
driver => "it87",
|
|
i2c_addrs => [0x20..0x2f],
|
|
i2c_detect => sub { ite_detect 0, @_ },
|
|
isa_addrs => [0x290],
|
|
isa_detect => sub { ite_isa_detect 0, @_ },
|
|
} ,
|
|
{
|
|
name => "SPD EEPROM",
|
|
driver => "eeprom",
|
|
i2c_addrs => [0x50..0x57],
|
|
i2c_detect => sub { eeprom_detect 0, @_ },
|
|
},
|
|
{
|
|
name => "Sony Vaio EEPROM",
|
|
driver => "eeprom",
|
|
i2c_addrs => [0x57],
|
|
i2c_detect => sub { eeprom_detect 1, @_ },
|
|
},
|
|
# Disabled by default (potentially dangerous)
|
|
# {
|
|
# name => "SPD EEPROM with Software Write-Protect",
|
|
# driver => "eeprom",
|
|
# i2c_addrs => [0x50..0x57],
|
|
# i2c_detect => sub { eeprom_detect 2, @_ },
|
|
# },
|
|
{
|
|
name => "DDC monitor",
|
|
driver => "ddcmon",
|
|
i2c_addrs => [0x50],
|
|
i2c_detect => sub { ddcmonitor_detect @_ },
|
|
},
|
|
{
|
|
name => "FSC Poseidon",
|
|
driver => "fscpos",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fscpos_detect @_ },
|
|
},
|
|
{
|
|
name => "FSC Scylla",
|
|
driver => "fscscy",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fscscy_detect @_ },
|
|
},
|
|
{
|
|
name => "FSC Hermes",
|
|
driver => "fscher",
|
|
i2c_addrs => [0x73],
|
|
i2c_detect => sub { fscher_detect @_ },
|
|
},
|
|
{
|
|
name => "ALi M5879",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x2c..0x2d],
|
|
i2c_detect => sub { m5879_detect @_},
|
|
} ,
|
|
{
|
|
name => "Philips Semiconductors SAA1064",
|
|
driver => "saa1064",
|
|
i2c_addrs => [0x38..0x3b],
|
|
i2c_detect => sub { saa1064_detect @_ },
|
|
},
|
|
{
|
|
name => "Philips Semiconductors PCA9540",
|
|
driver => "pca9540",
|
|
i2c_addrs => [0x70],
|
|
i2c_detect => sub { pca9540_detect @_ },
|
|
},
|
|
{
|
|
name => "Maxim MAX6900",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x50],
|
|
i2c_detect => sub { max6900_detect @_ },
|
|
},
|
|
{
|
|
name => "SMBus 2.0 ARP-Capable Device",
|
|
driver => "smbus-arp",
|
|
i2c_addrs => [0x61],
|
|
i2c_detect => sub { arp_detect @_},
|
|
},
|
|
{
|
|
name => "IPMI BMC KCS",
|
|
driver => "bmcsensors",
|
|
isa_addrs => [ 0x0ca0 ],
|
|
isa_detect => sub { ipmi_kcs_detect @_ },
|
|
},
|
|
{
|
|
name => "IPMI BMC SMIC",
|
|
driver => "bmcsensors",
|
|
isa_addrs => [ 0x0ca8 ],
|
|
isa_detect => sub { ipmi_smic_detect @_ },
|
|
},
|
|
{
|
|
name => "Smart Battery Charger",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x09],
|
|
i2c_detect => sub { smartbatt_chgr_detect @_},
|
|
},
|
|
{
|
|
name => "Smart Battery Manager/Selector",
|
|
driver => "to-be-written",
|
|
i2c_addrs => [0x0a],
|
|
i2c_detect => sub { smartbatt_mgr_detect @_},
|
|
},
|
|
{
|
|
name => "Smart Battery",
|
|
driver => "smartbatt",
|
|
i2c_addrs => [0x0b],
|
|
i2c_detect => sub { smartbatt_detect @_},
|
|
},
|
|
);
|
|
|
|
# This is a list of all recognized superio chips.
|
|
# Each entry must have the following fields:
|
|
# name: The full chip name
|
|
# driver: The driver name (without .o extension). Put in
|
|
# "to-be-written" if it is not yet available.
|
|
# Put in "not-a-sensor" if the chip doesn't have hardware monitoring
|
|
# capabilities (listing such chips here removes the need of manual
|
|
# lookup when people report them).
|
|
# enter: The password sequence to write to the address register
|
|
# devid: The device ID(s) we have to match (base device)
|
|
# logdev: The logical device containing the sensors
|
|
# exit (optional): Sequence to write to the address register to exit config
|
|
# mode. If not provided, a default reset operation is performed.
|
|
# alias_detect (optional): For chips which can be both on the ISA and the
|
|
# I2C bus, a function which detectes whether two entries are the same.
|
|
# The function should take three parameters: The ISA address, the
|
|
# I2C bus number, and the I2C address.
|
|
@superio_ids = (
|
|
{
|
|
enter => [0x87, 0x01, 0x55, 0x55],
|
|
chips =>
|
|
[
|
|
{
|
|
name => "ITE 8702F Super IO Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x8702,
|
|
logdev => 0x04,
|
|
},
|
|
{
|
|
name => "ITE 8705F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8705,
|
|
logdev => 0x04,
|
|
},
|
|
{
|
|
name => "ITE 8712F Super IO Sensors",
|
|
driver => "it87",
|
|
devid => 0x8712,
|
|
logdev => 0x04,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
enter => [],
|
|
chips =>
|
|
[
|
|
{
|
|
name => "Nat. Semi. PC87351 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0xe2,
|
|
logdev => 0x08,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87360 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe1,
|
|
logdev => 0x09,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87363 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe8,
|
|
logdev => 0x09,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87364 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe4,
|
|
logdev => 0x09,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87365 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe5,
|
|
logdev => 0x09,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87365 Super IO Voltage Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe5,
|
|
logdev => 0x0d,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87365 Super IO Thermal Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe5,
|
|
logdev => 0x0e,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87366 Super IO Fan Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe9,
|
|
logdev => 0x09,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87366 Super IO Voltage Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe9,
|
|
logdev => 0x0d,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87366 Super IO Thermal Sensors",
|
|
driver => "pc87360",
|
|
devid => 0xe9,
|
|
logdev => 0x0e,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87372 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0xf0,
|
|
logdev => 0x09,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC87373 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0xf3,
|
|
logdev => 0x09,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC8739x Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0xea,
|
|
},
|
|
{
|
|
name => "Nat. Semi. PC8741x Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0xee,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
enter => [0x55],
|
|
exit => [0xaa],
|
|
chips =>
|
|
[
|
|
{
|
|
name => "SMSC 47B27x Super IO Fan Sensors",
|
|
driver => "smsc47m1",
|
|
devid => 0x51,
|
|
logdev => 0x0a,
|
|
},
|
|
{
|
|
name => "SMSC 47M10x/13x Super IO Fan Sensors",
|
|
driver => "smsc47m1",
|
|
devid => 0x59,
|
|
logdev => 0x0a,
|
|
},
|
|
{
|
|
name => "SMSC 47M14x Super IO Fan Sensors",
|
|
driver => "smsc47m1",
|
|
devid => 0x5f,
|
|
logdev => 0x0a,
|
|
},
|
|
{
|
|
name => "SMSC 47M15x/192 Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x60,
|
|
logdev => 0x0a,
|
|
},
|
|
{
|
|
name => "SMSC 47S42x Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x57,
|
|
logdev => 0x0a,
|
|
},
|
|
{
|
|
name => "SMSC 47S45x Super IO Fan Sensors",
|
|
driver => "to-be-written",
|
|
devid => 0x62,
|
|
logdev => 0x0a,
|
|
},
|
|
{
|
|
name => "SMSC 47M172 Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x14,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
enter => [0x87, 0x87],
|
|
exit => [0xaa],
|
|
chips =>
|
|
[
|
|
{
|
|
name => "VT1211 Super IO Sensors",
|
|
driver => "vt1211",
|
|
devid => 0x3c,
|
|
logdev => 0x0b,
|
|
},
|
|
{
|
|
name => "Winbond W83627HF Super IO Sensors",
|
|
driver => "w83627hf",
|
|
devid => 0x52,
|
|
logdev => 0x0b,
|
|
},
|
|
{
|
|
name => "Winbond W83627THF Super IO Sensors",
|
|
driver => "w83627hf",
|
|
devid => 0x82,
|
|
logdev => 0x0b,
|
|
},
|
|
{
|
|
name => "Winbond W83637HF Super IO Sensors",
|
|
driver => "w83627hf",
|
|
devid => 0x70,
|
|
logdev => 0x0b,
|
|
},
|
|
{
|
|
name => "Winbond W83697HF Super IO Sensors",
|
|
driver => "w83627hf",
|
|
devid => 0x60,
|
|
logdev => 0x0b,
|
|
},
|
|
{
|
|
name => "Winbond W83697SF/UF Super IO PWM",
|
|
driver => "to-be-written",
|
|
devid => 0x68,
|
|
logdev => 0x0b,
|
|
},
|
|
{
|
|
name => "Winbond W83L517D Super IO",
|
|
driver => "not-a-sensor",
|
|
devid => 0x61,
|
|
},
|
|
],
|
|
},
|
|
);
|
|
|
|
#######################
|
|
# AUXILIARY FUNCTIONS #
|
|
#######################
|
|
|
|
sub swap_bytes
|
|
{
|
|
return (($_[0] & 0xff00) >> 8) + (($_[0] & 0x00ff) << 8)
|
|
}
|
|
|
|
# $_[0] is the sought value
|
|
# @_[1..] is the list to seek in
|
|
# Returns: 0 on failure, 1 if found.
|
|
sub contains
|
|
{
|
|
my $sought = shift;
|
|
foreach (@_) {
|
|
return 1 if $sought eq $_;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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 = oct $start if $start =~ /^0/;
|
|
if (defined $end) {
|
|
$end = oct $end if $end =~ /^0/;
|
|
$start = $min if $start < $min;
|
|
$end = $max if $end > $max;
|
|
push @res, ($start+0..$end+0);
|
|
} else {
|
|
push @res, $start+0 if $start >= $min and $start <= $max;
|
|
}
|
|
}
|
|
return sort { $a <=> $b } @res;
|
|
}
|
|
|
|
# @_[0]: Reference to list 1
|
|
# @_[1]: Reference to list 2
|
|
# Result: 0 if they have no elements in common, 1 if they have
|
|
# Elements must be numeric.
|
|
sub any_list_match
|
|
{
|
|
my ($list1,$list2) = @_;
|
|
my ($el1,$el2);
|
|
foreach $el1 (@$list1) {
|
|
foreach $el2 (@$list2) {
|
|
return 1 if $el1 == $el2;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
###################
|
|
# I/O port access #
|
|
###################
|
|
|
|
sub initialize_ioports
|
|
{
|
|
sysopen (IOPORTS, "/dev/port", O_RDWR)
|
|
or die "/dev/port: $!\n";
|
|
binmode IOPORTS;
|
|
}
|
|
|
|
sub close_ioports
|
|
{
|
|
close (IOPORTS)
|
|
or print "Warning: $!\n";
|
|
}
|
|
|
|
# $_[0]: port to read
|
|
# Returns: -1 on failure, read value on success.
|
|
sub inb
|
|
{
|
|
my ($res,$nrchars);
|
|
sysseek IOPORTS, $_[0], 0 or return -1;
|
|
$nrchars = sysread IOPORTS, $res, 1;
|
|
return -1 if not defined $nrchars or $nrchars != 1;
|
|
$res = unpack "C",$res ;
|
|
return $res;
|
|
}
|
|
|
|
# $_[0]: port to write
|
|
# $_[1]: value to write
|
|
# Returns: -1 on failure, 0 on success.
|
|
sub outb
|
|
{
|
|
if ($_[1] > 0xff)
|
|
{
|
|
my ($package, $filename, $line, $sub) = caller(1);
|
|
print "\n*** Called outb with value=$_[1] from line $line\n",
|
|
"*** (in $sub). PLEASE REPORT!\n",
|
|
"*** Terminating.\n";
|
|
exit(-1);
|
|
}
|
|
my $towrite = pack "C", $_[1];
|
|
sysseek IOPORTS, $_[0], 0 or return -1;
|
|
my $nrchars = syswrite IOPORTS, $towrite, 1;
|
|
return -1 if not defined $nrchars or $nrchars != 1;
|
|
return 0;
|
|
}
|
|
|
|
# $_[0]: Address register
|
|
# $_[1]: Data register
|
|
# $_[2]: Register to read
|
|
# Returns: read value
|
|
sub isa_read_byte
|
|
{
|
|
outb $_[0],$_[2];
|
|
return inb $_[1];
|
|
}
|
|
|
|
# $_[0]: Address register
|
|
# $_[1]: Data register
|
|
# $_[2]: Register to write
|
|
# $_[3}: Value to write
|
|
# Returns: nothing
|
|
sub isa_write_byte
|
|
{
|
|
outb $_[0],$_[2];
|
|
outb $_[1],$_[3];
|
|
}
|
|
|
|
#################
|
|
# AUTODETECTION #
|
|
#################
|
|
|
|
use vars qw($modules_conf $dev_i2c);
|
|
|
|
sub initialize_conf
|
|
{
|
|
my $use_devfs = 0;
|
|
open INPUTFILE, "/proc/mounts" or die "Can't access /proc/mounts!";
|
|
while (<INPUTFILE>) {
|
|
if (m@^\w+ /dev devfs @) {
|
|
$use_devfs = 1;
|
|
$dev_i2c = '/dev/i2c/';
|
|
last;
|
|
}
|
|
}
|
|
close INPUTFILE;
|
|
|
|
if (-f '/etc/modules.conf') {
|
|
$modules_conf = '/etc/modules.conf';
|
|
} elsif (-f '/etc/conf.modules') {
|
|
$modules_conf = '/etc/conf.modules';
|
|
} else { # default
|
|
$modules_conf = '/etc/modules.conf';
|
|
}
|
|
|
|
if (!$use_devfs) {
|
|
if (-c '/dev/i2c-0') {
|
|
$dev_i2c = '/dev/i2c-';
|
|
} else { # default
|
|
print "No i2c device files found. Use prog/mkdev/mkdev.sh to create them.\n";
|
|
exit -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
# [0] -> VERSION
|
|
# [1] -> PATCHLEVEL
|
|
# [2] -> SUBLEVEL
|
|
# [3] -> EXTRAVERSION
|
|
#
|
|
use vars qw(@kernel_version);
|
|
|
|
sub initialize_kernel_version
|
|
{
|
|
`uname -r` =~ /(\d+)\.(\d+)\.(\d+)(.*)/;
|
|
@kernel_version = ($1, $2, $3, $4);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
###########
|
|
# MODULES #
|
|
###########
|
|
|
|
use vars qw(%modules_list);
|
|
|
|
sub initialize_modules_list
|
|
{
|
|
open INPUTFILE, "/proc/modules" or die "Can't access /proc/modules!";
|
|
while (<INPUTFILE>) {
|
|
tr/_/-/;
|
|
$modules_list{$1} = 1 if m/^(\S*)/;
|
|
}
|
|
close INPUTFILE;
|
|
}
|
|
|
|
##############
|
|
# PCI ACCESS #
|
|
##############
|
|
|
|
use vars qw(%pci_list);
|
|
|
|
# This function returns a hash of hashes. Each hash has some PCI information
|
|
# (more than we will ever need, probably). The most important
|
|
# fields are 'bus', 'slot', 'func' (they uniquely identify a PCI device in
|
|
# a computer) and 'vendid','devid' (they uniquely identify a type of device).
|
|
sub read_proc_dev_pci
|
|
{
|
|
my ($dfn,$vend,%pci_list);
|
|
open INPUTFILE, "/proc/bus/pci/devices" or return;
|
|
while (<INPUTFILE>) {
|
|
my $record = {};
|
|
($dfn,$vend,$record->{irq},$record->{base_addr0},$record->{base_addr1},
|
|
$record->{base_addr2},$record->{base_addr3},$record->{base_addr4},
|
|
$record->{base_addr5},$record->{rom_base_addr}) =
|
|
map { oct "0x$_" } (split) [0..9];
|
|
$record->{bus} = $dfn >> 8;
|
|
$record->{slot} = ($dfn & 0xf8) >> 3;
|
|
$record->{func} = $dfn & 0x07;
|
|
$record->{vendid} = $vend >> 16;
|
|
$record->{devid} = $vend & 0xffff;
|
|
|
|
$pci_list{ sprintf("%04x:%04x",$record->{vendid},$record->{devid}) } =
|
|
$record;
|
|
}
|
|
close INPUTFILE or return;
|
|
return %pci_list;
|
|
}
|
|
|
|
sub initialize_proc_pci
|
|
{
|
|
%pci_list = read_proc_dev_pci;
|
|
die "Can't access /proc/bus/pci/devices!" if not defined %pci_list;
|
|
}
|
|
|
|
#####################
|
|
# ADAPTER DETECTION #
|
|
#####################
|
|
|
|
sub all_available_adapters
|
|
{
|
|
my @res = ();
|
|
foreach my $adapter (@pci_adapters) {
|
|
push @res, $adapter->{driver}
|
|
if exists $modules_list{$adapter->{driver}};
|
|
}
|
|
return @res;
|
|
}
|
|
|
|
sub adapter_pci_detection_sis_96x
|
|
{
|
|
my $driver="";
|
|
|
|
# first, determine which driver if any...
|
|
if (kernel_version_at_least(2,6,0)) {
|
|
if (exists $pci_list{"1039:0016"}) {
|
|
$driver = "i2c-sis96x";
|
|
} elsif (exists $pci_list{"1039:0008"}) {
|
|
$driver = "i2c-sis5595";
|
|
}
|
|
} elsif (kernel_version_at_least(2,4,0)) {
|
|
if (exists $pci_list{"1039:0008"}) {
|
|
if ((exists $pci_list{"1039:0645"}) ||
|
|
(exists $pci_list{"1039:0646"}) ||
|
|
(exists $pci_list{"1039:0648"}) ||
|
|
(exists $pci_list{"1039:0650"}) ||
|
|
(exists $pci_list{"1039:0651"}) ||
|
|
(exists $pci_list{"1039:0655"}) ||
|
|
(exists $pci_list{"1039:0735"}) ||
|
|
(exists $pci_list{"1039:0745"}) ||
|
|
(exists $pci_list{"1039:0746"})) {
|
|
$driver = "i2c-sis645";
|
|
} else {
|
|
$driver = "i2c-sis5595";
|
|
}
|
|
} elsif ((exists $pci_list{"1039:0016"}) ||
|
|
(exists $pci_list{"1039:0018"})) {
|
|
$driver = "i2c-sis645";
|
|
}
|
|
}
|
|
|
|
# then, add the appropriate entries to @pci_adapters
|
|
if ($driver eq "i2c-sis5595") {
|
|
push @pci_adapters, @pci_adapters_sis5595;
|
|
} elsif ($driver eq "i2c-sis645") {
|
|
push @pci_adapters, @pci_adapters_sis645;
|
|
} elsif ($driver eq "i2c-sis96x") {
|
|
push @pci_adapters, @pci_adapters_sis96x;
|
|
}
|
|
}
|
|
|
|
sub adapter_pci_detection
|
|
{
|
|
my ($key,$device,$try,@res);
|
|
print "Probing for PCI bus adapters...\n";
|
|
|
|
# Custom detection routine for some SiS chipsets
|
|
adapter_pci_detection_sis_96x();
|
|
|
|
# Generic detection loop
|
|
while ( ($key, $device) = each %pci_list) {
|
|
foreach $try (@pci_adapters) {
|
|
if ((defined($device->{vendid}) and
|
|
$try->{vendid} == $device->{vendid} and
|
|
$try->{devid} == $device->{devid} and
|
|
$try->{func} == $device->{func}) or
|
|
(! defined($device->{vendid}) and
|
|
$device->{desc} =~ /$try->{procid}/ and
|
|
$try->{func} == $device->{func})) {
|
|
printf "Use driver `%s' for device %02x:%02x.%x: %s\n",
|
|
$try->{driver}?$try->{driver}:"<To Be Written>",
|
|
$device->{bus},$device->{slot},$device->{func},$try->{procid};
|
|
push @res,$try->{driver};
|
|
}
|
|
}
|
|
}
|
|
if (! @res) {
|
|
print ("Sorry, no PCI bus adapters found.\n");
|
|
} else {
|
|
printf ("Probe succesfully concluded.\n");
|
|
}
|
|
return @res;
|
|
}
|
|
|
|
# $_[0]: Adapter description as found in /proc/bus/i2c
|
|
# $_[1]: Algorithm description as found in /proc/bus/i2c
|
|
sub find_adapter_driver
|
|
{
|
|
my $adapter;
|
|
for $adapter (@pci_adapters) {
|
|
return $adapter->{driver} if &{$adapter->{match}} ($_[0],$_[1]);
|
|
}
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
#############################
|
|
# I2C AND SMBUS /DEV ACCESS #
|
|
#############################
|
|
|
|
# This should really go into a separate module/package.
|
|
|
|
# To do: support i2c-level access (through sysread/syswrite, probably).
|
|
# I can't test this at all (PIIX4 does not support this), so I have not
|
|
# included it.
|
|
|
|
use vars qw($IOCTL_I2C_RETRIES $IOCTL_I2C_TIMEOUT $IOCTL_I2C_UDELAY
|
|
$IOCTL_I2C_MDELAY $IOCTL_I2C_SLAVE $IOCTL_I2C_TENBIT
|
|
$IOCTL_I2C_SMBUS);
|
|
|
|
# These are copied from <linux/i2c.h> and <linux/smbus.h>
|
|
|
|
# For bit-adapters:
|
|
$IOCTL_I2C_RETRIES = 0x0701;
|
|
$IOCTL_I2C_TIMEOUT = 0x0702;
|
|
$IOCTL_I2C_UDELAY = 0x0705;
|
|
$IOCTL_I2C_MDELAY = 0x0706;
|
|
|
|
# General ones:
|
|
$IOCTL_I2C_SLAVE = 0x0703;
|
|
$IOCTL_I2C_TENBIT = 0x0704;
|
|
$IOCTL_I2C_SMBUS = 0x0720;
|
|
|
|
|
|
|
|
use vars qw($SMBUS_READ $SMBUS_WRITE $SMBUS_QUICK $SMBUS_BYTE $SMBUS_BYTE_DATA
|
|
$SMBUS_WORD_DATA $SMBUS_PROC_CALL $SMBUS_BLOCK_DATA);
|
|
|
|
# These are copied from <linux/smbus.h>
|
|
|
|
$SMBUS_READ = 1;
|
|
$SMBUS_WRITE = 0;
|
|
$SMBUS_QUICK = 0;
|
|
$SMBUS_BYTE = 1;
|
|
$SMBUS_BYTE_DATA = 2;
|
|
$SMBUS_WORD_DATA = 3;
|
|
$SMBUS_PROC_CALL = 4;
|
|
$SMBUS_BLOCK_DATA = 5;
|
|
|
|
# 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) = @_;
|
|
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.
|
|
# Exact calling conventions are intricate; read i2c-dev.c if you really need
|
|
# to know.
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: $SMBUS_READ for reading, $SMBUS_WRITE for writing
|
|
# $_[2]: Command (usually register number)
|
|
# $_[3]: Transaction kind ($SMBUS_BYTE, $SMBUS_BYTE_DATA, etc.)
|
|
# $_[4]: Reference to an array used for input/output of data
|
|
# Returns: 0 on failure, 1 on success.
|
|
# Note that we need to get back to Integer boundaries through the 'x2'
|
|
# in the pack. This is very compiler-dependent; I wish there was some other
|
|
# way to do this.
|
|
sub i2c_smbus_access
|
|
{
|
|
my ($file,$read_write,$command,$size,$data) = @_;
|
|
my $data_array = pack "C32", @$data;
|
|
my $ioctl_data = pack "C2x2Ip", ($read_write,$command,$size,$data_array);
|
|
ioctl $file, $IOCTL_I2C_SMBUS, $ioctl_data or return 0;
|
|
$_[4] = [ unpack "C32",$data_array ];
|
|
return 1;
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Either 0 or 1
|
|
# Returns: -1 on failure, the 0 on success.
|
|
sub i2c_smbus_write_quick
|
|
{
|
|
my ($file,$value) = @_;
|
|
my $data = [];
|
|
i2c_smbus_access $file, $value, 0, $SMBUS_QUICK, $data
|
|
or return -1;
|
|
return 0;
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# Returns: -1 on failure, the read byte on success.
|
|
sub i2c_smbus_read_byte
|
|
{
|
|
my ($file) = @_;
|
|
my $data = [];
|
|
i2c_smbus_access $file, $SMBUS_READ, 0, $SMBUS_BYTE, $data
|
|
or return -1;
|
|
return $$data[0];
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Byte to write
|
|
# Returns: -1 on failure, 0 on success.
|
|
sub i2c_smbus_write_byte
|
|
{
|
|
my ($file,$command) = @_;
|
|
my $data = [$command];
|
|
i2c_smbus_access $file, $SMBUS_WRITE, 0, $SMBUS_BYTE, $data
|
|
or return -1;
|
|
return 0;
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Command byte (usually register number)
|
|
# Returns: -1 on failure, the read byte on success.
|
|
sub i2c_smbus_read_byte_data
|
|
{
|
|
my ($file,$command) = @_;
|
|
my $data = [];
|
|
i2c_smbus_access $file, $SMBUS_READ, $command, $SMBUS_BYTE_DATA, $data
|
|
or return -1;
|
|
return $$data[0];
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Command byte (usually register number)
|
|
# $_[2]: Byte to write
|
|
# Returns: -1 on failure, 0 on success.
|
|
sub i2c_smbus_write_byte_data
|
|
{
|
|
my ($file,$command,$value) = @_;
|
|
my $data = [$value];
|
|
i2c_smbus_access $file, $SMBUS_WRITE, $command, $SMBUS_BYTE_DATA, $data
|
|
or return -1;
|
|
return 0;
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Command byte (usually register number)
|
|
# Returns: -1 on failure, the read word on success.
|
|
# Note: some devices use the wrong endiannes; use swap_bytes to correct for
|
|
# this.
|
|
sub i2c_smbus_read_word_data
|
|
{
|
|
my ($file,$command) = @_;
|
|
my $data = [];
|
|
i2c_smbus_access $file, $SMBUS_READ, $command, $SMBUS_WORD_DATA, $data
|
|
or return -1;
|
|
return $$data[0] + 256 * $$data[1];
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Command byte (usually register number)
|
|
# $_[2]: Byte to write
|
|
# Returns: -1 on failure, 0 on success.
|
|
# Note: some devices use the wrong endiannes; use swap_bytes to correct for
|
|
# this.
|
|
sub i2c_smbus_write_word_data
|
|
{
|
|
my ($file,$command,$value) = @_;
|
|
my $data = [$value & 0xff, $value >> 8];
|
|
i2c_smbus_access $file, $SMBUS_WRITE, $command, $SMBUS_WORD_DATA, $data
|
|
or return -1;
|
|
return 0;
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Command byte (usually register number)
|
|
# $_[2]: Word to write
|
|
# Returns: -1 on failure, read word on success.
|
|
# Note: some devices use the wrong endiannes; use swap_bytes to correct for
|
|
# this.
|
|
sub i2c_smbus_process_call
|
|
{
|
|
my ($file,$command,$value) = @_;
|
|
my $data = [$value & 0xff, $value >> 8];
|
|
i2c_smbus_access $file, $SMBUS_WRITE, $command, $SMBUS_PROC_CALL, $data
|
|
or return -1;
|
|
return $$data[0] + 256 * $$data[1];
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Command byte (usually register number)
|
|
# Returns: Undefined on failure, a list of read bytes on success
|
|
# Note: some devices use the wrong endiannes; use swap_bytes to correct for
|
|
# this.
|
|
sub i2c_smbus_read_block_data
|
|
{
|
|
my ($file,$command) = @_;
|
|
my $data = [];
|
|
i2c_smbus_access $file, $SMBUS_READ, $command, $SMBUS_BLOCK_DATA, $data
|
|
or return;
|
|
shift @$data;
|
|
return @$data;
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Command byte (usually register number)
|
|
# @_[2..]: List of values to write
|
|
# Returns: -1 on failure, 0 on success.
|
|
# Note: some devices use the wrong endiannes; use swap_bytes to correct for
|
|
# this.
|
|
sub i2c_smbus_write_block_data
|
|
{
|
|
my ($file,$command,@data) = @_;
|
|
i2c_smbus_access $file, $SMBUS_WRITE, $command, $SMBUS_BLOCK_DATA, \@data
|
|
or return;
|
|
return 0;
|
|
}
|
|
|
|
# $_[0]: Reference to an opened filehandle
|
|
# $_[1]: Address
|
|
# 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) = @_;
|
|
my $data = [];
|
|
if (($addr >= 0x50 && $addr <= 0x5F)
|
|
|| ($addr >= 0x30 && $addr <= 0x37)) {
|
|
# This covers all EEPROMs we know of, including page protection addresses.
|
|
# Note that some page protection addresses will not reveal themselves with
|
|
# this, because they ack on write only, but this is probably better since
|
|
# some EEPROMs write-protect themselves permanently on almost any write to
|
|
# their page protection address.
|
|
return i2c_smbus_access($file, $SMBUS_READ, 0, $SMBUS_BYTE, $data);
|
|
} else {
|
|
return i2c_smbus_access($file, $SMBUS_WRITE, 0, $SMBUS_QUICK, $data);
|
|
}
|
|
}
|
|
|
|
####################
|
|
# ADAPTER SCANNING #
|
|
####################
|
|
|
|
use vars qw(@chips_detected);
|
|
|
|
# We will build a complicated structure @chips_detected here, being:
|
|
# A list of
|
|
# references to hashes
|
|
# with field 'driver', being a string with the driver name for this chip;
|
|
# with field 'detected'
|
|
# being a reference to a list of
|
|
# references to hashes of type 'detect_data';
|
|
# with field 'misdetected'
|
|
# being a reference to a list of
|
|
# references to hashes of type 'detect_data'
|
|
|
|
# Type detect_data:
|
|
# A hash
|
|
# with field 'i2c_adap' containing an adapter string as appearing
|
|
# in /proc/bus/i2c (if this is an I2C detection)
|
|
# with field 'i2c_algo' containing an algorithm string as appearing
|
|
# in /proc/bus/i2c (if this is an I2C detection)
|
|
# with field 'i2c_devnr', contianing the /dev/i2c-* number of this
|
|
# adapter (if this is an I2C detection)
|
|
# with field 'i2c_driver', containing the driver name for this adapter
|
|
# (if this is an I2C detection)
|
|
# with field 'i2c_addr', containing the I2C address of the detection;
|
|
# (if this is an I2C detection)
|
|
# with field 'i2c_sub_addrs', containing a reference to a list of
|
|
# other I2C addresses (if this is an I2C detection)
|
|
# with field 'i2c_extra' if this is an I2C detection and the address
|
|
# is not normally probed by the kernel driver
|
|
# with field 'isa_addr' containing the ISA address this chip is on
|
|
# (if this is an ISA detection)
|
|
# with field 'isa_extra' if this is an ISA detection and the address
|
|
# is not normally probed by the kernel driver
|
|
# with field 'conf', containing the confidence level of this detection
|
|
# with field 'chipname', containing the chip name
|
|
|
|
# This adds a detection to the above structure. We do no alias detection
|
|
# here; so you should do ISA detections *after* all I2C detections.
|
|
# Not all possibilities of i2c_addr and i2c_sub_addrs are exhausted.
|
|
# In all normal cases, it should be all right.
|
|
# $_[0]: chip driver
|
|
# $_[1]: reference to data hash
|
|
# Returns: Nothing
|
|
sub add_i2c_to_chips_detected
|
|
{
|
|
my ($chipdriver,$datahash) = @_;
|
|
my ($i,$new_detected_ref,$new_misdetected_ref,$detected_ref,$misdetected_ref,
|
|
$main_entry,$detected_entry,$put_in_detected,@hash_addrs,@entry_addrs,
|
|
$do_not_add);
|
|
|
|
# First determine where the hash has to be added.
|
|
for ($i = 0; $i < @chips_detected; $i++) {
|
|
last if ($chips_detected[$i]->{driver} eq $chipdriver);
|
|
}
|
|
if ($i == @chips_detected) {
|
|
push @chips_detected, { driver => $chipdriver,
|
|
detected => [],
|
|
misdetected => [] };
|
|
}
|
|
$new_detected_ref = $chips_detected[$i]->{detected};
|
|
$new_misdetected_ref = $chips_detected[$i]->{misdetected};
|
|
|
|
# Find out whether our new entry should go into the detected or the
|
|
# misdetected list. We compare all i2c addresses; if at least one matches,
|
|
# but our conf value is lower, we assume this is a misdetect.
|
|
@hash_addrs = ($datahash->{i2c_addr});
|
|
push @hash_addrs, @{$datahash->{i2c_sub_addrs}}
|
|
if exists $datahash->{i2c_sub_addrs};
|
|
$put_in_detected = 1;
|
|
$do_not_add = 0;
|
|
FIND_LOOP:
|
|
foreach $main_entry (@chips_detected) {
|
|
foreach $detected_entry (@{$main_entry->{detected}}) {
|
|
@entry_addrs = ($detected_entry->{i2c_addr});
|
|
push @entry_addrs, @{$detected_entry->{i2c_sub_addrs}}
|
|
if exists $detected_entry->{i2c_sub_addrs};
|
|
if ($detected_entry->{i2c_devnr} == $datahash->{i2c_devnr} and
|
|
any_list_match \@entry_addrs, \@hash_addrs) {
|
|
if ($detected_entry->{conf} >= $datahash->{conf}) {
|
|
$put_in_detected = 0;
|
|
}
|
|
if ($chipdriver eq $main_entry->{driver}) {
|
|
$do_not_add = 1;
|
|
}
|
|
last FIND_LOOP;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($put_in_detected) {
|
|
# Here, we move all entries from detected to misdetected which
|
|
# match at least in one main or sub address. This may not be the
|
|
# best idea to do, as it may remove detections without replacing
|
|
# them with second-best ones. Too bad.
|
|
# (Khali 2003-09-13) If the driver is the same, the "misdetected"
|
|
# entry is simply deleted; failing to do so cause the configuration
|
|
# lines generated later to look very confusing (the driver will
|
|
# be told to ignore valid addresses).
|
|
@hash_addrs = ($datahash->{i2c_addr});
|
|
push @hash_addrs, @{$datahash->{i2c_sub_addrs}}
|
|
if exists $datahash->{i2c_sub_addrs};
|
|
foreach $main_entry (@chips_detected) {
|
|
$detected_ref = $main_entry->{detected};
|
|
$misdetected_ref = $main_entry->{misdetected};
|
|
for ($i = @$detected_ref-1; $i >=0; $i--) {
|
|
@entry_addrs = ($detected_ref->[$i]->{i2c_addr});
|
|
push @entry_addrs, @{$detected_ref->[$i]->{i2c_sub_addrs}}
|
|
if exists $detected_ref->[$i]->{i2c_sub_addrs};
|
|
if ($detected_ref->[$i]->{i2c_devnr} == $datahash->{i2c_devnr} and
|
|
any_list_match \@entry_addrs, \@hash_addrs) {
|
|
push @$misdetected_ref,$detected_ref->[$i]
|
|
unless $chipdriver eq $main_entry->{driver};
|
|
splice @$detected_ref, $i, 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
# Now add the new entry to detected
|
|
push @$new_detected_ref, $datahash;
|
|
} else {
|
|
# No hard work here
|
|
push @$new_misdetected_ref, $datahash
|
|
unless $do_not_add;
|
|
}
|
|
}
|
|
|
|
# This adds a detection to the above structure. We also do alias detection
|
|
# here; so you should do ISA detections *after* all I2C detections.
|
|
# $_[0]: alias detection function
|
|
# $_[1]: chip driver
|
|
# $_[2]: reference to data hash
|
|
# Returns: 0 if it is not an alias, datahash reference if it is.
|
|
sub add_isa_to_chips_detected
|
|
{
|
|
my ($alias_detect,$chipdriver,$datahash) = @_;
|
|
my ($i,$new_detected_ref,$new_misdetected_ref,$detected_ref,$misdetected_ref,
|
|
$main_entry,$isalias);
|
|
|
|
# First determine where the hash has to be added.
|
|
$isalias=0;
|
|
for ($i = 0; $i < @chips_detected; $i++) {
|
|
last if ($chips_detected[$i]->{driver} eq $chipdriver);
|
|
}
|
|
if ($i == @chips_detected) {
|
|
push @chips_detected, { driver => $chipdriver,
|
|
detected => [],
|
|
misdetected => [] };
|
|
}
|
|
$new_detected_ref = $chips_detected[$i]->{detected};
|
|
$new_misdetected_ref = $chips_detected[$i]->{misdetected};
|
|
|
|
# Now, we are looking for aliases. An alias can only be the same chiptype.
|
|
# If an alias is found in the misdetected list, we add the new information
|
|
# and terminate this function. If it is found in the detected list, we
|
|
# still have to check whether another chip has claimed this ISA address.
|
|
# So we remove the old entry from the detected list and put it in datahash.
|
|
|
|
# Misdetected alias detection:
|
|
for ($i = 0; $i < @$new_misdetected_ref; $i++) {
|
|
if (exists $new_misdetected_ref->[$i]->{i2c_addr} and
|
|
not exists $new_misdetected_ref->[$i]->{isa_addr} and
|
|
defined $alias_detect and
|
|
$new_misdetected_ref->[$i]->{chipname} eq $datahash->{chipname}) {
|
|
open FILE,"$dev_i2c$new_misdetected_ref->[$i]->{i2c_devnr}" or
|
|
print("Can't open $dev_i2c$new_misdetected_ref->[$i]->{i2c_devnr}?!?\n"),
|
|
next;
|
|
binmode FILE;
|
|
i2c_set_slave_addr \*FILE,$new_misdetected_ref->[$i]->{i2c_addr} or
|
|
print("Can't set I2C address for ",
|
|
"$dev_i2c$new_misdetected_ref->[$i]->{i2c_devnr}?!?\n"),
|
|
next;
|
|
if (&$alias_detect ($datahash->{isa_addr},\*FILE,
|
|
$new_misdetected_ref->[$i]->{i2c_addr})) {
|
|
$new_misdetected_ref->[$i]->{isa_addr} = $datahash->{isa_addr};
|
|
$new_misdetected_ref->[$i]->{isa_extra} = $datahash->{isa_extra}
|
|
if exists $datahash->{isa_extra};
|
|
close FILE;
|
|
return $new_misdetected_ref->[$i];
|
|
}
|
|
close FILE;
|
|
}
|
|
}
|
|
|
|
# Detected alias detection:
|
|
for ($i = 0; $i < @$new_detected_ref; $i++) {
|
|
if (exists $new_detected_ref->[$i]->{i2c_addr} and
|
|
not exists $new_detected_ref->[$i]->{isa_addr} and
|
|
defined $alias_detect and
|
|
$new_detected_ref->[$i]->{chipname} eq $datahash->{chipname}) {
|
|
open FILE,"$dev_i2c$new_detected_ref->[$i]->{i2c_devnr}" or
|
|
print("Can't open $dev_i2c$new_detected_ref->[$i]->{i2c_devnr}?!?\n"),
|
|
next;
|
|
binmode FILE;
|
|
i2c_set_slave_addr \*FILE,$new_detected_ref->[$i]->{i2c_addr} or
|
|
print("Can't set I2C address for ",
|
|
"$dev_i2c$new_detected_ref->[$i]->{i2c_devnr}?!?\n"),
|
|
next;
|
|
if (&$alias_detect ($datahash->{isa_addr},\*FILE,
|
|
$new_detected_ref->[$i]->{i2c_addr})) {
|
|
$new_detected_ref->[$i]->{isa_addr} = $datahash->{isa_addr};
|
|
$new_detected_ref->[$i]->{isa_extra} = $datahash->{isa_extra}
|
|
if exists $datahash->{isa_extra};
|
|
($datahash) = splice (@$new_detected_ref, $i, 1);
|
|
close FILE;
|
|
$isalias=1;
|
|
last;
|
|
}
|
|
close FILE;
|
|
}
|
|
}
|
|
|
|
|
|
# Find out whether our new entry should go into the detected or the
|
|
# misdetected list. We only compare main isa_addr here, of course.
|
|
# (Khali 2004-05-12) If the driver is the same, the "misdetected"
|
|
# entry is simply deleted; same we do for I2C chips.
|
|
foreach $main_entry (@chips_detected) {
|
|
$detected_ref = $main_entry->{detected};
|
|
$misdetected_ref = $main_entry->{misdetected};
|
|
for ($i = 0; $i < @{$main_entry->{detected}}; $i++) {
|
|
if (exists $detected_ref->[$i]->{isa_addr} and
|
|
$detected_ref->[$i]->{isa_addr} == $datahash->{isa_addr}) {
|
|
if ($detected_ref->[$i]->{conf} >= $datahash->{conf}) {
|
|
push @$new_misdetected_ref, $datahash
|
|
unless $main_entry->{driver} eq $chipdriver;
|
|
} else {
|
|
push @$misdetected_ref,$detected_ref->[$i]
|
|
unless $main_entry->{driver} eq $chipdriver;
|
|
splice @$detected_ref, $i,1;
|
|
push @$new_detected_ref, $datahash;
|
|
}
|
|
if ($isalias) {
|
|
return $datahash;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
# Not found? OK, put it in the detected list
|
|
push @$new_detected_ref, $datahash;
|
|
if ($isalias) {
|
|
return $datahash;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
# $_[0]: The number of the adapter to scan
|
|
# $_[1]: The name of the adapter, as appearing in /proc/bus/i2c
|
|
# $_[2]: The name of the algorithm, as appearing in /proc/bus/i2c
|
|
# $_[3]: The driver of the adapter
|
|
# @_[4..]: Addresses not to scan
|
|
sub scan_adapter
|
|
{
|
|
my ( $adapter_nr,$adapter_name,$algorithm_name,$adapter_driver,
|
|
$not_to_scan) = @_;
|
|
my ($chip, $addr, $conf,@chips,$new_hash,$other_addr);
|
|
|
|
# As we modify it, we need a copy
|
|
my @not_to_scan = @$not_to_scan;
|
|
|
|
open FILE,"$dev_i2c$adapter_nr" or
|
|
(print "Can't open $dev_i2c$adapter_nr\n"), return;
|
|
binmode FILE;
|
|
|
|
# Now scan each address in turn
|
|
foreach $addr (0x03..0x77) {
|
|
# As the not_to_scan list is sorted, we can check it fast
|
|
if (@not_to_scan and $not_to_scan[0] == $addr) {
|
|
shift @not_to_scan;
|
|
next;
|
|
}
|
|
|
|
i2c_set_slave_addr(\*FILE,$addr) or
|
|
printf("Client at address 0x%02x can not be probed - unload all client drivers first!\n",$addr), next;
|
|
|
|
next unless i2c_probe(\*FILE, $addr);
|
|
printf "Client found at address 0x%02x\n",$addr;
|
|
|
|
foreach $chip (@chip_ids) {
|
|
if (exists $$chip{i2c_addrs} and contains $addr, @{$$chip{i2c_addrs}}) {
|
|
print "Probing for `$$chip{name}'... ";
|
|
if (($conf,@chips) = &{$$chip{i2c_detect}} (\*FILE ,$addr)) {
|
|
print "Success!\n",
|
|
" (confidence $conf, driver `$$chip{driver}')";
|
|
if (@chips) {
|
|
print ", other addresses:";
|
|
@chips = sort @chips;
|
|
foreach $other_addr (sort @chips) {
|
|
printf(" 0x%02x",$other_addr);
|
|
}
|
|
}
|
|
printf "\n";
|
|
$new_hash = { conf => $conf,
|
|
i2c_addr => $addr,
|
|
chipname => $$chip{name},
|
|
i2c_adap => $adapter_name,
|
|
i2c_algo => $algorithm_name,
|
|
i2c_driver => $adapter_driver,
|
|
i2c_devnr => $adapter_nr,
|
|
};
|
|
if (@chips) {
|
|
my @chips_copy = @chips;
|
|
$new_hash->{i2c_sub_addrs} = \@chips_copy;
|
|
}
|
|
$new_hash->{i2c_extra} = 0
|
|
if exists $chip->{i2c_driver_addrs} and
|
|
not contains( $addr , @{$chip->{i2c_driver_addrs}});
|
|
add_i2c_to_chips_detected $$chip{driver}, $new_hash;
|
|
} else {
|
|
print "Failed!\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sub scan_isa_bus
|
|
{
|
|
my ($chip,$addr,$conf);
|
|
foreach $chip (@chip_ids) {
|
|
next if not exists $$chip{isa_addrs} or not exists $$chip{isa_detect};
|
|
print "Probing for `$$chip{name}'\n";
|
|
foreach $addr (@{$$chip{isa_addrs}}) {
|
|
if ($addr) {
|
|
printf " Trying address 0x%04x... ", $addr;
|
|
} else {
|
|
print " Trying general detect... ";
|
|
}
|
|
$conf = &{$$chip{isa_detect}} ($addr);
|
|
print("Failed!\n"), next if not defined $conf;
|
|
print "Success!\n";
|
|
printf " (confidence %d, driver `%s')\n", $conf, $$chip{driver};
|
|
my $new_hash = { conf => $conf,
|
|
isa_addr => $addr,
|
|
chipname => $$chip{name}
|
|
};
|
|
$new_hash->{isa_extra} = 0
|
|
if exists $chip->{isa_driver_addrs} and
|
|
not contains ($addr, @{$chip->{isa_driver_addrs}});
|
|
$new_hash = add_isa_to_chips_detected $$chip{alias_detect},$$chip{driver},
|
|
$new_hash;
|
|
if ($new_hash) {
|
|
printf " Alias of the chip on I2C bus `%s', address 0x%04x\n",
|
|
$new_hash->{i2c_adap},$new_hash->{i2c_addr};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sub exit_superio
|
|
{
|
|
my ($addrreg, $datareg, $family, $success) = @_;
|
|
|
|
# If detection succeeded and an exit sequence exists, use it
|
|
if ($success && defined ($$family{exit})) {
|
|
foreach my $byte (@{$$family{exit}}) {
|
|
outb($addrreg, $byte);
|
|
}
|
|
return;
|
|
}
|
|
|
|
# Else return to "Wait For Key" state (PNP-ISA spec)
|
|
outb($addrreg, 0x02);
|
|
outb($datareg, 0x02);
|
|
}
|
|
|
|
# The following are taken from the PNP ISA spec (so it's supposed
|
|
# to be common to all Super I/0 chips):
|
|
# devidreg: The device ID register(s)
|
|
# logdevreg: The logical device register
|
|
# actreg: The activation register within the logical device
|
|
# actmask: The activation bit in the activation register
|
|
# basereg: The I/O base register within the logical device
|
|
sub scan_superio
|
|
{
|
|
my ($addrreg, $datareg) = @_;
|
|
my ($val, $addr);
|
|
|
|
my %superio = (
|
|
devidreg => 0x20,
|
|
logdevreg => 0x07,
|
|
actreg => 0x30,
|
|
actmask => 0x01,
|
|
basereg => 0x60,
|
|
);
|
|
|
|
FAMILY:
|
|
foreach my $family (@superio_ids) {
|
|
# write the password
|
|
foreach $val (@{$$family{enter}}) {
|
|
outb($addrreg, $val);
|
|
}
|
|
foreach my $chip (@{$$family{chips}}) {
|
|
print "Probing for `$$chip{name}'\n";
|
|
# check the device ID
|
|
outb($addrreg, $superio{devidreg});
|
|
$val = inb($datareg);
|
|
if ($val == 0x00 || $val == 0xff) {
|
|
print " Failed! (skipping family)\n";
|
|
exit_superio($addrreg, $datareg, $family, 0);
|
|
next FAMILY;
|
|
}
|
|
if ($$chip{devid}>0xff) {
|
|
outb($addrreg, $superio{devidreg} + 1);
|
|
$val = ($val << 8) | inb($datareg);
|
|
}
|
|
if ($val != $$chip{devid}) {
|
|
printf " Failed! (0x%02x)\n", $val;
|
|
next;
|
|
}
|
|
print " Success...";
|
|
# does it have hardware monitoring capabilities
|
|
if($$chip{driver} eq "not-a-sensor") {
|
|
print " (no hardware monitoring capabilities)\n";
|
|
next;
|
|
}
|
|
# switch to the sensor logical device
|
|
outb($addrreg, $superio{logdevreg});
|
|
outb($datareg, $$chip{logdev});
|
|
# check the activation register
|
|
outb($addrreg, $superio{actreg});
|
|
$val = inb($datareg);
|
|
if(!($val & $superio{actmask})) {
|
|
print " but not activated\n";
|
|
next;
|
|
}
|
|
# Get the IO base register
|
|
outb($addrreg, $superio{basereg});
|
|
$addr = inb($datareg);
|
|
outb($addrreg, $superio{basereg} + 1);
|
|
$addr = ($addr << 8) | inb($datareg);
|
|
if($addr == 0) {
|
|
print " but no address specified\n";
|
|
next;
|
|
}
|
|
printf " found at address 0x%04x\n", $addr;
|
|
my $new_hash = { conf => 9,
|
|
isa_addr => $addr,
|
|
chipname => $$chip{name}
|
|
};
|
|
add_isa_to_chips_detected $$chip{alias_detect},$$chip{driver},
|
|
$new_hash;
|
|
}
|
|
exit_superio($addrreg, $datareg, $family, 1);
|
|
}
|
|
}
|
|
|
|
|
|
##################
|
|
# CHIP DETECTION #
|
|
##################
|
|
|
|
# Each function returns a confidence value. The higher this value, the more
|
|
# sure we are about this chip. A Winbond W83781D, for example, will be
|
|
# detected as a LM78 too; but as the Winbond detection has a higher confidence
|
|
# factor, you should identify it as a Winbond.
|
|
|
|
# Each function returns a list. The first element is the confidence value;
|
|
# Each element after it is an SMBus address. In this way, we can detect
|
|
# chips with several SMBus addresses. The SMBus address for which the
|
|
# function was called is never returned.
|
|
|
|
# If there are devices which get confused if they are only read from, then
|
|
# this program will surely confuse them. But we guarantee never to write to
|
|
# any of these devices.
|
|
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (7) if detected.
|
|
# Registers used: 0x58
|
|
sub mtp008_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
return if (i2c_smbus_read_byte_data($file,0x58)) != 0xac;
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = LM78, 1 = LM78-J, 2 = LM79)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, (6) if detected.
|
|
# Registers used:
|
|
# 0x40: Configuration
|
|
# 0x48: Full I2C Address
|
|
# 0x49: Device ID
|
|
# Note that this function is always called through a closure, so the
|
|
# arguments are shifted by one place.
|
|
sub lm78_detect
|
|
{
|
|
my $reg;
|
|
my ($chip,$file,$addr) = @_;
|
|
return unless i2c_smbus_read_byte_data($file,0x48) == $addr;
|
|
return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00;
|
|
$reg = i2c_smbus_read_byte_data($file,0x49);
|
|
return unless ($chip == 0 and ($reg == 0x00 or $reg == 0x20)) or
|
|
($chip == 1 and $reg == 0x40) or
|
|
($chip == 2 and ($reg & 0xfe) == 0xc0);
|
|
return (6);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = LM78, 1 = LM78-J, 2 = LM79)
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, 7 if detected.
|
|
# Note: Only address 0x290 is scanned at this moment.
|
|
sub lm78_isa_detect
|
|
{
|
|
my ($chip,$addr) = @_ ;
|
|
my $val = inb ($addr + 1);
|
|
return if inb ($addr + 2) != $val or inb ($addr + 3) != $val or
|
|
inb ($addr + 7) != $val;
|
|
|
|
$val = inb($addr + 5) & 0x7f;
|
|
outb($addr + 5, ~$val & 0xff);
|
|
if ((inb ($addr+5) & 0x7f) != (~ $val & 0x7f)) {
|
|
outb($addr+5,$val);
|
|
return;
|
|
}
|
|
my $readproc = sub { isa_read_byte $addr + 5, $addr + 6, @_ };
|
|
return unless (&$readproc(0x40) & 0x80) == 0x00;
|
|
my $reg = &$readproc(0x49);
|
|
return unless ($chip == 0 and ($reg == 0x00 or $reg == 0x20)) or
|
|
($chip == 1 and $reg == 0x40) or
|
|
($chip == 2 and ($reg & 0xfe) == 0xc0);
|
|
return 7;
|
|
}
|
|
|
|
|
|
# $_[0]: Chip to detect (0 = LM78, 1 = LM78-J, 2 = LM79)
|
|
# $_[1]: ISA address
|
|
# $_[2]: I2C file handle
|
|
# $_[3]: I2C address
|
|
sub lm78_alias_detect
|
|
{
|
|
my ($chip,$isa_addr,$file,$i2c_addr) = @_;
|
|
my $i;
|
|
my $readproc = sub { isa_read_byte $isa_addr + 5, $isa_addr + 6, @_ };
|
|
return 0 unless &$readproc(0x48) == $i2c_addr;
|
|
for ($i = 0x2b; $i <= 0x3d; $i ++) {
|
|
return 0 unless &$readproc($i) == i2c_smbus_read_byte_data($file,$i);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, 2 to 6 if detected;
|
|
# 6 means that the LM75 was found in its default power up state;
|
|
# 5 means that the LM75 was found in another known default state;
|
|
# 4 means that the LM75 was found in a probable state (temperatures
|
|
# make sense);
|
|
# 3 means that the configuration register passes the test;
|
|
# Registers used:
|
|
# 0x00: Temperature
|
|
# 0x01: Configuration
|
|
# 0x02: Hysteresis
|
|
# 0x03: Overtemperature Shutdown
|
|
# The first detection step is only based on the fact that the LM75 has only
|
|
# four registers. Any other chip in the valid address range with only four
|
|
# registers will be detected too.
|
|
# Note that register $00 may change, so we can't use the modulo trick on it.
|
|
sub lm75_detect
|
|
{
|
|
my $i;
|
|
my ($file,$addr) = @_;
|
|
my $cur = i2c_smbus_read_word_data($file,0x00);
|
|
my $cur_varies = 0;
|
|
my $conf = i2c_smbus_read_byte_data($file,0x01);
|
|
my $hyst = i2c_smbus_read_word_data($file,0x02);
|
|
my $os = i2c_smbus_read_word_data($file,0x03);
|
|
for ($i = 0x00; $i <= 0x1f; $i += 1) {
|
|
return if i2c_smbus_read_byte_data($file,($i * 0x08) + 0x01) != $conf;
|
|
return if i2c_smbus_read_word_data($file,($i * 0x08) + 0x02) != $hyst;
|
|
return if i2c_smbus_read_word_data($file,($i * 0x08) + 0x03) != $os;
|
|
$cur_varies = 1
|
|
if (! $cur_varies) and
|
|
i2c_smbus_read_word_data($file,($i * 0x08)) != $cur;
|
|
}
|
|
# Default power up state (from specs)
|
|
return 6
|
|
if $conf == 0 and ($hyst&0x80ff) == 0x004b and ($os&0x80ff) == 0x0050;
|
|
# Default state as seen on the Asus TX97-E
|
|
return 5
|
|
if $conf == 0 and ($hyst&0x80ff) == 0x002d and ($os&0x80ff) == 0x0034;
|
|
# All registers hold the same value, obviously a misdetection
|
|
return
|
|
if (! $cur_varies) and $conf == ($cur&0xff) and $cur == $hyst
|
|
and $cur == $os;
|
|
# Unused bits in conf register
|
|
return
|
|
if ($conf&0xe0) != 0;
|
|
# Most probable value ranges
|
|
return 4
|
|
if (($cur&0x00ff) <= 125 || ($cur&0x00ff) >= 201)
|
|
and ($hyst&0x00ff) <= 125 and ($os&0x00ff) <= 125 and $hyst<$os;
|
|
return 3;
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = LM92, 1 = LM76, 2 = MAX6633/MAX6634/MAX6635)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 2 or 4 if detected;
|
|
# Registers used:
|
|
# 0x01: Configuration (National Semiconductor only)
|
|
# 0x02: Hysteresis
|
|
# 0x03: Critical Temp
|
|
# 0x04: Low Limit
|
|
# 0x05: High Limit
|
|
# 0x07: Manufacturer ID (LM92 only)
|
|
# One detection step is based on the fact that the LM92 and clones have a
|
|
# limited number of registers, which cycle modulo 16 address values.
|
|
# Note that register 0x00 may change, so we can't use the modulo trick on it.
|
|
sub lm92_detect
|
|
{
|
|
my ($chip, $file, $addr) = @_;
|
|
|
|
my $cur = i2c_smbus_read_word_data($file, 0x00);
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x01);
|
|
my $hyst = i2c_smbus_read_word_data($file, 0x02);
|
|
my $crit = i2c_smbus_read_word_data($file, 0x03);
|
|
my $low = i2c_smbus_read_word_data($file, 0x04);
|
|
my $high = i2c_smbus_read_word_data($file, 0x05);
|
|
|
|
return if $chip == 0
|
|
and i2c_smbus_read_word_data($file, 0x07) != 0x0180;
|
|
|
|
return if ($chip == 0 || $chip == 1)
|
|
and ($conf & 0xE0);
|
|
|
|
for (my $i = 0; $i < 8; $i++) {
|
|
return if i2c_smbus_read_byte_data($file, $i*16+0x01) != $conf;
|
|
return if i2c_smbus_read_word_data($file, $i*16+0x02) != $hyst;
|
|
return if i2c_smbus_read_word_data($file, $i*16+0x03) != $crit;
|
|
return if i2c_smbus_read_word_data($file, $i*16+0x04) != $low;
|
|
return if i2c_smbus_read_word_data($file, $i*16+0x05) != $high;
|
|
}
|
|
|
|
foreach my $temp ($hyst, $crit, $low, $high) {
|
|
return if $chip == 2 and ($temp & 0x7F00);
|
|
return if $chip != 2 and ($temp & 0x0700);
|
|
}
|
|
|
|
return 4 if $chip == 0;
|
|
return 2;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (3) if detected,
|
|
# (5) or (7) if even more bits match.
|
|
# Registers used:
|
|
# 0xAA: Temperature
|
|
# 0xA1: High limit
|
|
# 0xA2: Low limit
|
|
# 0xAC: Configuration
|
|
# Detection is weak. We check if Bit 3 is set and Bit 2 is clear.
|
|
# The DS1621 will aways have a config like 0x????10??. A even better
|
|
# match would be 0x0??01000.
|
|
# Temperature checkings will hopefully prevent LM75 chips from being
|
|
# detected as a DS1621.
|
|
sub ds1621_detect
|
|
{
|
|
my $i;
|
|
my ($file,$addr) = @_;
|
|
my $temp = i2c_smbus_read_word_data($file,0xAA);
|
|
return if ($temp & 0x007F);
|
|
$temp = i2c_smbus_read_word_data($file,0xA1);
|
|
return if ($temp & 0x007F);
|
|
$temp = i2c_smbus_read_word_data($file,0xA2);
|
|
return if ($temp & 0x007F);
|
|
my $conf = i2c_smbus_read_byte_data($file,0xAC);
|
|
return (7) if ($conf & 0x9F) == 0x98;
|
|
return (5) if ($conf & 0x0F) == 0x08;
|
|
return (3) if ($conf & 0x0C) == 0x08;
|
|
return ;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, 1 to 3 if detected.
|
|
# Registers used:
|
|
# 0x00: Configuration register
|
|
# 0x02: Interrupt state register
|
|
# 0x2a-0x3d: Limits registers
|
|
# This one is easily misdetected since it doesn't provide identification
|
|
# registers. So we have to use some tricks:
|
|
# - 6-bit addressing, so limits readings modulo 0x40 should be unchanged
|
|
# - positive temperature limits
|
|
# - limits order correctness
|
|
# Hopefully this should limit the rate of false positives, without increasing
|
|
# the rate of false negatives.
|
|
# Thanks to Lennard Klein for testing on a non-LM80 chip, which was
|
|
# previously misdetected, and isn't anymore. For reference, it scored
|
|
# a final confidence of 0, and changing from strict limit comparisons
|
|
# to loose comparisons did not change the score.
|
|
sub lm80_detect
|
|
{
|
|
my ($i,$reg);
|
|
my ($file,$addr) = @_;
|
|
|
|
return if (i2c_smbus_read_byte_data($file,0x00) & 0x80) != 0;
|
|
return if (i2c_smbus_read_byte_data($file,0x02) & 0xc0) != 0;
|
|
|
|
for ($i = 0x2a; $i <= 0x3d; $i++) {
|
|
$reg = i2c_smbus_read_byte_data($file,$i);
|
|
return if i2c_smbus_read_byte_data($file,$i+0x40) != $reg;
|
|
return if i2c_smbus_read_byte_data($file,$i+0x80) != $reg;
|
|
return if i2c_smbus_read_byte_data($file,$i+0xc0) != $reg;
|
|
}
|
|
|
|
# Refine a bit by checking wether limits are in the correct order
|
|
# (min<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;
|
|
}
|
|
|
|
# $_[0]: Chip to detect
|
|
# (0 = LM83, 1 = LM82)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 5 to 8 if detected.
|
|
# Registers used:
|
|
# 0x02: Status 1
|
|
# 0x03: Configuration
|
|
# 0x04: Company ID of LM84
|
|
# 0x35: Status 2
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: Chip ID / die revision
|
|
# We can use the LM84 Company ID register because the LM83 and the LM82 are
|
|
# compatible with the LM84.
|
|
# The LM83 chip ID is missing from the datasheet and was contributed by
|
|
# Magnus Forsstrom.
|
|
sub lm83_detect
|
|
{
|
|
my ($chip, $file) = @_;
|
|
return if i2c_smbus_read_byte_data($file,0xfe) != 0x01;
|
|
return if $chip == 0 and i2c_smbus_read_byte_data($file,0xff) != 0x03;
|
|
return if $chip == 1 and i2c_smbus_read_byte_data($file,0xff) != 0x01;
|
|
|
|
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 $chip == 0
|
|
&& (i2c_smbus_read_byte_data($file,0x35) & 0x48) == 0x00;
|
|
|
|
return $confidence;
|
|
}
|
|
|
|
# $_[0]: Chip to detect
|
|
# (0 = LM90, 1=LM89/LM99, 2=LM86, 3=ADM1032, 4=MAX6657/MAX6658/MAX6659,
|
|
# 5 = ADT7461)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 3, 6 or 8 if detected.
|
|
# The Maxim chips have a low confidence value (3)
|
|
# because the die revision codes are not known.
|
|
# Registers used:
|
|
# 0x03: Configuration
|
|
# 0x04: Conversion rate
|
|
# 0xfe: Manufacturer ID
|
|
# 0xff: Chip ID / die revision
|
|
sub lm90_detect
|
|
{
|
|
my ($chip, $file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0xfe);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0xff);
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x03);
|
|
my $rate = i2c_smbus_read_byte_data($file, 0x04);
|
|
|
|
if ($chip == 0) {
|
|
return if ($conf & 0x2a) != 0;
|
|
return if $rate > 0x09;
|
|
return if $mid != 0x01; # National Semiconductor
|
|
return 8 if $cid == 0x21; # LM90
|
|
return 6 if ($cid & 0x0f) == 0x20;
|
|
}
|
|
if ($chip == 1) {
|
|
return if ($conf & 0x2a) != 0;
|
|
return if $rate > 0x09;
|
|
return if $mid != 0x01; # National Semiconductor
|
|
return 8 if $addr == 0x4c and $cid == 0x31; # LM89/LM99
|
|
return 8 if $addr == 0x4d and $cid == 0x34; # LM89-1/LM99-1
|
|
return 6 if ($cid & 0x0f) == 0x30;
|
|
}
|
|
if ($chip == 2) {
|
|
return if ($conf & 0x2a) != 0;
|
|
return if $rate > 0x09;
|
|
return if $mid != 0x01; # National Semiconductor
|
|
return 8 if $cid == 0x11; # LM86
|
|
return 6 if ($cid & 0xf0) == 0x10;
|
|
}
|
|
if ($chip == 3) {
|
|
return if ($conf & 0x3f) != 0;
|
|
return if $rate > 0x0a;
|
|
return if $mid != 0x41; # Analog Devices
|
|
return 8 if ($cid & 0xf0) == 0x40; # ADM1032
|
|
}
|
|
if ($chip == 4) {
|
|
return if ($conf & 0x1f) != 0;
|
|
return if $rate > 0x09;
|
|
return if $mid != 0x4d; # Maxim
|
|
return 3;
|
|
}
|
|
if ($chip == 5) {
|
|
return if ($conf & 0x1b) != 0;
|
|
return if $rate > 0x0a;
|
|
return if $mid != 0x41; # Analog Devices
|
|
return 8 if $cid == 0x61; # ADT7461
|
|
}
|
|
return;
|
|
}
|
|
|
|
# $_[0]: Chip to detect
|
|
# (0 = ADM1029)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 3 to 9 if detected.
|
|
# Registers used:
|
|
# 0x02, 0x03: Fan support
|
|
# 0x05: GPIO config
|
|
# 0x07, 0x08, 0x09: Fan config
|
|
# 0x0d: Manufacturer ID
|
|
# 0x0e: Chip ID / die revision
|
|
sub adm1029_detect
|
|
{
|
|
my ($chip, $file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0x0d);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x0e);
|
|
my $fansc = i2c_smbus_read_byte_data($file, 0x02);
|
|
my $fanss = i2c_smbus_read_byte_data($file, 0x03);
|
|
my $gpio = i2c_smbus_read_byte_data($file, 0x05);
|
|
my $fanas = i2c_smbus_read_byte_data($file, 0x07);
|
|
my $fanhps = i2c_smbus_read_byte_data($file, 0x08);
|
|
my $fanfs = i2c_smbus_read_byte_data($file, 0x09);
|
|
my $confidence = 3;
|
|
|
|
if ($chip == 0) {
|
|
return if $mid != 0x41; # Analog Devices
|
|
$confidence++ if ($cid & 0xF0) == 0x00; # ADM1029
|
|
$confidence+=2 if ($fansc & 0xFC) == 0x00
|
|
&& ($fanss & 0xFC) == 0x00;
|
|
$confidence+=2 if ($fanas & 0xFC) == 0x00
|
|
&& ($fanhps & 0xFC) == 0x00
|
|
&& ($fanfs & 0xFC) == 0x00;
|
|
$confidence++ if ($gpio & 0x80) == 0x00;
|
|
return $confidence;
|
|
}
|
|
return;
|
|
}
|
|
|
|
# $_[0]: Chip to detect
|
|
# (0 = ADM1030, 1=ADM1031)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 3 to 7 (ADM1031) or 9 (ADM1030)
|
|
# if detected.
|
|
# Registers used:
|
|
# 0x01: Config 2
|
|
# 0x03: Status 2
|
|
# 0x0d, 0x0e, 0x0f: Temperature offsets
|
|
# 0x22: Fan speed config
|
|
# 0x3d: Chip ID
|
|
# 0x3e: Manufacturer ID
|
|
# 0x3f: Die revision
|
|
sub adm1031_detect
|
|
{
|
|
my ($chip, $file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x3d);
|
|
my $drev = i2c_smbus_read_byte_data($file, 0x3f);
|
|
my $conf2 = i2c_smbus_read_byte_data($file, 0x01);
|
|
my $stat2 = i2c_smbus_read_byte_data($file, 0x03);
|
|
my $fsc = i2c_smbus_read_byte_data($file, 0x22);
|
|
my $lto = i2c_smbus_read_byte_data($file, 0x0d);
|
|
my $r1to = i2c_smbus_read_byte_data($file, 0x0e);
|
|
my $r2to = i2c_smbus_read_byte_data($file, 0x0f);
|
|
my $confidence = 3;
|
|
|
|
if ($chip == 0) {
|
|
return if $mid != 0x41; # Analog Devices
|
|
return if $cid != 0x30; # ADM1030
|
|
$confidence++ if ($drev & 0x70) == 0x00;
|
|
$confidence++ if ($conf2 & 0x4A) == 0x00;
|
|
$confidence++ if ($stat2 & 0x3F) == 0x00;
|
|
$confidence++ if ($fsc & 0xF0) == 0x00;
|
|
$confidence++ if ($lto & 0x70) == 0x00;
|
|
$confidence++ if ($r1to & 0x70) == 0x00;
|
|
return $confidence;
|
|
}
|
|
if ($chip == 1) {
|
|
return if $mid != 0x41; # Analog Devices
|
|
return if $cid != 0x31; # ADM1031
|
|
$confidence++ if ($drev & 0x70) == 0x00;
|
|
$confidence++ if ($lto & 0x70) == 0x00;
|
|
$confidence++ if ($r1to & 0x70) == 0x00;
|
|
$confidence++ if ($r2to & 0x70) == 0x00;
|
|
return $confidence;
|
|
}
|
|
return;
|
|
}
|
|
|
|
# $_[0]: Chip to detect
|
|
# (0 = ADT7467)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 5 if detected.
|
|
# Registers used:
|
|
# 0x3d: Chip ID
|
|
# 0x3e: Manufacturer ID
|
|
# 0x3f: Die revision
|
|
sub adt7467_detect
|
|
{
|
|
my ($chip, $file, $addr) = @_;
|
|
my $mid = i2c_smbus_read_byte_data($file, 0x3e);
|
|
my $cid = i2c_smbus_read_byte_data($file, 0x3d);
|
|
my $drev = i2c_smbus_read_byte_data($file, 0x3f);
|
|
|
|
if ($chip == 0) {
|
|
return if $mid != 0x41; # Analog Devices
|
|
return if $cid != 0x68; # ADT7467
|
|
return if ($drev & 0xf0) != 0x70;
|
|
return 5;
|
|
}
|
|
return
|
|
}
|
|
|
|
# $_[0]: Vendor to check for
|
|
# (0x01 = National Semi, 0x41 = Analog Dev, 0x5c = SMSC)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# #_[2]: Base address.
|
|
# Returns: undef if not detected, (7) or (8) if detected.
|
|
# Registers used: 0x3e == Vendor register.
|
|
# 0x3d == Device ID register (Analog Devices only).
|
|
# 0x3f == Version/Stepping register.
|
|
# Constants used: 0x01 == National Semiconductor Vendor Id.
|
|
# 0x41 == Analog Devices Vendor Id.
|
|
# 0x5c == SMSC Vendor Id.
|
|
# 0x60 == Version number. The lower 4 stepping
|
|
# bits are masked and ignored.
|
|
sub lm85_detect
|
|
{
|
|
my ($vendor,$file,$addr) = @_;
|
|
return if (i2c_smbus_read_byte_data($file,0x3e)) != $vendor ;
|
|
return if (i2c_smbus_read_byte_data($file,0x3f) & 0xf0) != 0x60;
|
|
|
|
if ($vendor == 0x41) # Analog Devices
|
|
{
|
|
return if i2c_smbus_read_byte_data($file, 0x3d) != 0x27;
|
|
return (8);
|
|
}
|
|
|
|
return (7);
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (7) if detected.
|
|
# Registers used: 0x3E, 0x3F
|
|
# Assume lower 2 bits of reg 0x3F are for revisions.
|
|
sub lm87_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
return if (i2c_smbus_read_byte_data($file,0x3e)) != 0x02;
|
|
return if (i2c_smbus_read_byte_data($file,0x3f) & 0xfc) != 0x04;
|
|
return (7);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = W83781D, 1 = W83782D, 2 = W83783S,
|
|
# 3 = W83627HF, 4 = AS99127F (rev.1),
|
|
# 5 = AS99127F (rev.2), 6 = ASB100, 7 = W83791D
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, (8,addr1,addr2) if detected, but only
|
|
# if the LM75 chip emulation is enabled.
|
|
# Registers used:
|
|
# 0x48: Full I2C Address
|
|
# 0x4a: I2C addresses of emulated LM75 chips
|
|
# 0x4e: Vendor ID byte selection, and bank selection
|
|
# 0x4f: Vendor ID
|
|
# 0x58: Device ID (only when in bank 0)
|
|
# Note: Fails if the W8378xD is not in bank 0!
|
|
# Note: Detection overrules a previous LM78 detection
|
|
# Note: Asus chips do not have their I2C address at register 0x48?
|
|
# AS99127F rev.1 and ASB100 have 0x00, confirmation wanted for
|
|
# AS99127F rev.2.
|
|
sub w83781d_detect
|
|
{
|
|
my ($reg1,$reg2,@res);
|
|
my ($chip,$file,$addr) = @_;
|
|
|
|
return unless (i2c_smbus_read_byte_data($file,0x48) == $addr)
|
|
or ($chip >= 4 && $chip <= 6);
|
|
|
|
$reg1 = i2c_smbus_read_byte_data($file,0x4e);
|
|
$reg2 = i2c_smbus_read_byte_data($file,0x4f);
|
|
if ($chip == 4) { # Asus AS99127F (rev.1)
|
|
return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xc3) or
|
|
(($reg1 & 0x80) == 0x80 and $reg2 == 0x12);
|
|
} elsif ($chip == 6) { # Asus ASB100
|
|
return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0x94) or
|
|
(($reg1 & 0x80) == 0x80 and $reg2 == 0x06);
|
|
} else { # Winbond and Asus AS99127F (rev.2)
|
|
return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xa3) or
|
|
(($reg1 & 0x80) == 0x80 and $reg2 == 0x5c);
|
|
}
|
|
|
|
return unless ($reg1 & 0x07) == 0x00;
|
|
|
|
$reg1 = i2c_smbus_read_byte_data($file,0x58);
|
|
return if $chip == 0 and ($reg1 != 0x10 && $reg1 != 0x11);
|
|
return if $chip == 1 and $reg1 != 0x30;
|
|
return if $chip == 2 and $reg1 != 0x40;
|
|
return if $chip == 3 and $reg1 != 0x21;
|
|
return if $chip == 4 and $reg1 != 0x31;
|
|
return if $chip == 5 and $reg1 != 0x31;
|
|
return if $chip == 6 and $reg1 != 0x31;
|
|
return if $chip == 7 and $reg1 != 0x71;
|
|
$reg1 = i2c_smbus_read_byte_data($file,0x4a);
|
|
@res = (8);
|
|
@res = (7) # Asus chips were always seen at 0x2d
|
|
if ($chip >= 4 && $chip <= 6 && $addr != 0x2d);
|
|
push @res, ($reg1 & 0x07) + 0x48 unless $reg1 & 0x08;
|
|
push @res, (($reg1 & 0x70) >> 4) + 0x48 unless ($reg1 & 0x80 or $chip == 2);
|
|
return @res;
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = ASM58, 1 = AS2K129R, 2 = ???)
|
|
# $_[1]: A reference to the file descriptor to access this chip
|
|
# $_[2]: Address (unused)
|
|
# Returns: undef if not detected, 5 if detected
|
|
# Registers used:
|
|
# 0x4e: Vendor ID high byte
|
|
# 0x4f: Vendor ID low byte
|
|
# 0x58: Device ID
|
|
# Note: The values were given by Alex van Kaam, we don't have datasheets
|
|
# to confirm.
|
|
sub mozart_detect
|
|
{
|
|
my ($vid,$dev);
|
|
my ($chip,$file,$addr) = @_;
|
|
|
|
$vid = (i2c_smbus_read_byte_data($file,0x4e) << 8)
|
|
+ i2c_smbus_read_byte_data($file,0x4f);
|
|
$dev = i2c_smbus_read_byte_data($file,0x58);
|
|
|
|
return if ($chip == 0) and ($dev != 0x56 || $vid != 0x9436);
|
|
return if ($chip == 1) and ($dev != 0x56 || $vid != 0x9406);
|
|
return if ($chip == 2) and ($dev != 0x10 || $vid != 0x5ca3);
|
|
|
|
return 5;
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = W83781D, 1 = W83782D, 2 = W83783S, 3 = W83627HF)
|
|
# $_[1]: ISA address
|
|
# $_[2]: I2C file handle
|
|
# $_[3]: I2C address
|
|
sub w83781d_alias_detect
|
|
{
|
|
my ($chip,$isa_addr,$file,$i2c_addr) = @_;
|
|
my $i;
|
|
my $readproc = sub { isa_read_byte $isa_addr + 5, $isa_addr + 6, @_ };
|
|
return 0 unless &$readproc(0x48) == $i2c_addr;
|
|
for ($i = 0x2b; $i <= 0x3d; $i ++) {
|
|
return 0 unless &$readproc($i) == i2c_smbus_read_byte_data($file,$i);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = W83781D, 1 = W83782D, 3 = W83627HF, 5 = W83697HF)
|
|
# (W83783S and AS99127F not on ISA bus)
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (8) if detected.
|
|
sub w83781d_isa_detect
|
|
{
|
|
my ($chip,$addr) = @_ ;
|
|
my ($reg1,$reg2);
|
|
my $val = inb ($addr + 1);
|
|
return if inb ($addr + 2) != $val or inb ($addr + 3) != $val or
|
|
inb ($addr + 7) != $val;
|
|
|
|
$val = inb($addr + 5) & 0x7f;
|
|
outb($addr+5, ~$val & 0xff);
|
|
if ((inb ($addr+5) & 0x7f) != (~ $val & 0x7f)) {
|
|
outb($addr+5,$val);
|
|
return;
|
|
}
|
|
|
|
my $read_proc = sub { isa_read_byte $addr + 5, $addr + 6, @_ };
|
|
$reg1 = &$read_proc(0x4e);
|
|
$reg2 = &$read_proc(0x4f);
|
|
return unless (($reg1 & 0x80) == 0x00 and $reg2 == 0xa3) or
|
|
(($reg1 & 0x80) == 0x80 and $reg2 == 0x5c);
|
|
return unless ($reg1 & 0x07) == 0x00;
|
|
$reg1 = &$read_proc(0x58) & 0xfe;
|
|
return if $chip == 0 and $reg1 != 0x10;
|
|
return if $chip == 1 and $reg1 != 0x30;
|
|
return if $chip == 3 and $reg1 != 0x20;
|
|
return if $chip == 5 and $reg1 != 0x60;
|
|
return 8;
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = Revision 0x00, 1 = Revision 0x80)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, (6) if detected.
|
|
# Registers used:
|
|
# 0x00: Device ID
|
|
# 0x01: Revision ID
|
|
# 0x03: Configuration
|
|
# Mediocre detection
|
|
sub gl518sm_detect
|
|
{
|
|
my $reg;
|
|
my ($chip,$file,$addr) = @_;
|
|
return unless i2c_smbus_read_byte_data($file,0x00) == 0x80;
|
|
return unless (i2c_smbus_read_byte_data($file,0x03) & 0x80) == 0x00;
|
|
$reg = i2c_smbus_read_byte_data($file,0x01);
|
|
return unless ($chip == 0 and $reg == 0x00) or
|
|
($chip == 1 and $reg == 0x80);
|
|
return (6);
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (5) if detected.
|
|
# Registers used:
|
|
# 0x00: Device ID
|
|
# 0x01: Revision ID
|
|
# 0x03: Configuration
|
|
# Mediocre detection
|
|
sub gl520sm_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
return unless i2c_smbus_read_byte_data($file,0x00) == 0x20;
|
|
return unless (i2c_smbus_read_byte_data($file,0x03) & 0x80) == 0x00;
|
|
# The line below must be better checked before I dare to use it.
|
|
# return unless i2c_smbus_read_byte_data($file,0x01) == 0x00;
|
|
return (5);
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (5) if detected.
|
|
# Registers used:
|
|
# 0x00: Device ID
|
|
# Mediocre detection
|
|
sub gl525sm_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
return unless i2c_smbus_read_byte_data($file,0x00) == 0x25;
|
|
return (5);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = ADM9240, 1 = DS1780, 2 = LM81)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, (7) if detected.
|
|
# Registers used:
|
|
# 0x3e: Company ID
|
|
# 0x40: Configuration
|
|
# 0x48: Full I2C Address
|
|
# Note: Detection overrules a previous LM78 detection
|
|
sub adm9240_detect
|
|
{
|
|
my $reg;
|
|
my ($chip, $file,$addr) = @_;
|
|
$reg = i2c_smbus_read_byte_data($file,0x3e);
|
|
return unless ($chip == 0 and $reg == 0x23) or
|
|
($chip == 1 and $reg == 0xda) or
|
|
($chip == 2 and $reg == 0x01);
|
|
return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00;
|
|
return unless i2c_smbus_read_byte_data($file,0x48) == $addr;
|
|
|
|
return (7);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = ADM1022, 1 = THMC50, 2 = ADM1028)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, (8) if detected.
|
|
# Registers used:
|
|
# 0x3e: Company ID
|
|
# 0x3f: Revision
|
|
# 0x40: Configuration
|
|
# Note: Detection overrules a previous LM78 or ADM9240 detection
|
|
sub adm1022_detect
|
|
{
|
|
my $reg;
|
|
my ($chip, $file,$addr) = @_;
|
|
$reg = i2c_smbus_read_byte_data($file,0x3e);
|
|
return unless ($chip == 0 and $reg == 0x41) or
|
|
($chip == 1 and $reg == 0x49) or
|
|
($chip == 2 and $reg == 0x41);
|
|
return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00;
|
|
$reg = i2c_smbus_read_byte_data($file, 0x3f);
|
|
return unless ($reg & 0xc0) == 0xc0;
|
|
return if $chip == 0 and ($reg & 0xc0) != 0xc0;
|
|
return if $chip == 2 and ($reg & 0xc0) == 0xc0;
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = ADM1025, 1 = NE1619)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, (8) if detected.
|
|
# Registers used:
|
|
# 0x3e: Company ID
|
|
# 0x3f: Revision
|
|
# 0x40: Configuration
|
|
# Note: Detection overrules a previous LM78 or ADM9240 detection
|
|
sub adm1025_detect
|
|
{
|
|
my $reg;
|
|
my ($chip, $file,$addr) = @_;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file,0x3e);
|
|
return if ($chip == 0) and ($reg != 0x41);
|
|
return if ($chip == 1) and ($reg != 0xA1);
|
|
|
|
return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00;
|
|
return unless (i2c_smbus_read_byte_data($file,0x3f) & 0xf0) == 0x20;
|
|
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = ADM1026)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, (8) if detected.
|
|
# Registers used:
|
|
# 0x16: Company ID
|
|
# 0x17: Revision
|
|
sub adm1026_detect
|
|
{
|
|
my $reg;
|
|
my ($chip, $file,$addr) = @_;
|
|
$reg = i2c_smbus_read_byte_data($file,0x16);
|
|
return unless ($reg == 0x41);
|
|
return unless (i2c_smbus_read_byte_data($file,0x17) & 0xf0) == 0x40;
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = ADM1024)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, (8) if detected.
|
|
# Registers used:
|
|
# 0x3e: Company ID
|
|
# 0x3f: Revision
|
|
# 0x40: Configuration
|
|
sub adm1024_detect
|
|
{
|
|
my $reg;
|
|
my ($chip, $file,$addr) = @_;
|
|
$reg = i2c_smbus_read_byte_data($file,0x3e);
|
|
return unless ($reg == 0x41);
|
|
return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00;
|
|
return unless (i2c_smbus_read_byte_data($file,0x3f) & 0xf0) == 0x10;
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: Chip to detect
|
|
# (0 = ADM1021, 1 = ADM1021A/ADM1023, 2 = MAX1617, 3 = MAX1617A, 4 = THMC10,
|
|
# 5 = LM84, 6 = GL523, 7 = MC1066)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 3 if simply detected, 5 if detected and
|
|
# manufacturer ID matches, 7 if detected and manufacturer ID and
|
|
# revision match
|
|
# Registers used:
|
|
# 0x04: Company ID (LM84 only)
|
|
# 0xfe: Company ID (all but LM84 and MAX1617)
|
|
# 0xff: Revision (ADM1021, ADM1021A/ADM1023 and MAX1617A)
|
|
# 0x02: Status
|
|
# 0x03: Configuration
|
|
# 0x04: Conversion rate
|
|
# 0x00-0x01, 0x05-0x08: Temperatures (MAX1617 and LM84)
|
|
# Note: Especially the MAX1617 has very bad detection; we give it a low
|
|
# confidence value.
|
|
sub adm1021_detect
|
|
{
|
|
my ($chip, $file, $addr) = @_;
|
|
my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
|
|
my $rev = i2c_smbus_read_byte_data($file, 0xff);
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x03);
|
|
my $status = i2c_smbus_read_byte_data($file, 0x02);
|
|
my $convrate = i2c_smbus_read_byte_data($file, 0x04);
|
|
|
|
# Check manufacturer IDs and product revisions when available
|
|
return if $chip == 0 and $man_id != 0x41 ||
|
|
($rev & 0xf0) != 0x00;
|
|
return if $chip == 1 and $man_id != 0x41 ||
|
|
($rev & 0xf0) != 0x30;
|
|
return if $chip == 3 and $man_id != 0x4d ||
|
|
$rev != 0x01;
|
|
return if $chip == 4 and $man_id != 0x49;
|
|
return if $chip == 5 and $convrate != 0x00;
|
|
return if $chip == 6 and $man_id != 0x23;
|
|
return if $chip == 7 and $man_id != 0x54;
|
|
|
|
# Check unused bits
|
|
if ($chip == 5) # LM84
|
|
{
|
|
return if ($status & 0xab) != 0;
|
|
return if ($conf & 0x7f) != 0;
|
|
}
|
|
else
|
|
{
|
|
return if ($status & 0x03) != 0;
|
|
return if ($conf & 0x3f) != 0;
|
|
return if ($convrate & 0xf8) != 0;
|
|
}
|
|
|
|
# Extra checks for MAX1617 and LM84, since those are often misdetected
|
|
# We verify several assertions (6 for the MAX1617, 4 for the LM84) and
|
|
# discard the chip if any fail. Note that these checks are not done
|
|
# by the adm1021 driver.
|
|
if ($chip == 2 || $chip == 5)
|
|
{
|
|
my $lte = i2c_smbus_read_byte_data($file, 0x00);
|
|
my $rte = i2c_smbus_read_byte_data($file, 0x01);
|
|
my $lhi = i2c_smbus_read_byte_data($file, 0x05);
|
|
my $rhi = i2c_smbus_read_byte_data($file, 0x07);
|
|
my $llo = i2c_smbus_read_byte_data($file, 0x06);
|
|
my $rlo = i2c_smbus_read_byte_data($file, 0x08);
|
|
|
|
# If all registers hold the same value, it has to be a misdetection
|
|
return if $lte == $rte and $lte == $lhi and $lte == $rhi
|
|
and $lte == $llo and $lte == $rlo;
|
|
|
|
# Negative temperatures
|
|
return if ($lte & 0x80) or ($rte & 0x80);
|
|
# Negative high limits
|
|
return if ($lhi & 0x80) or ($rhi & 0x80);
|
|
# Low limits over high limits
|
|
if ($chip != 5) # LM84 doesn't have low limits
|
|
{
|
|
$llo-=256 if ($llo & 0x80);
|
|
$rlo-=256 if ($rlo & 0x80);
|
|
return if ($llo > $lhi) or ($rlo > $rhi);
|
|
}
|
|
}
|
|
|
|
return 3 if ($chip == 2) or ($chip == 5);
|
|
return 7 if $chip <= 3;
|
|
return 5;
|
|
}
|
|
|
|
# $_[0]: Chip to detect
|
|
# (0 = MAX1619)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 7 if detected
|
|
# Registers used:
|
|
# 0xfe: Company ID
|
|
# 0xff: Device ID
|
|
# 0x02: Status
|
|
# 0x03: Configuration
|
|
# 0x04: Conversion rate
|
|
sub max1619_detect
|
|
{
|
|
my ($chip, $file, $addr) = @_;
|
|
my $man_id = i2c_smbus_read_byte_data($file, 0xfe);
|
|
my $dev_id = i2c_smbus_read_byte_data($file, 0xff);
|
|
my $conf = i2c_smbus_read_byte_data($file, 0x03);
|
|
my $status = i2c_smbus_read_byte_data($file, 0x02);
|
|
my $convrate = i2c_smbus_read_byte_data($file, 0x04);
|
|
|
|
return if $man_id != 0x4D
|
|
or $dev_id != 0x04
|
|
or ($conf & 0x03)
|
|
or ($status & 0x61)
|
|
or $convrate >= 8;
|
|
|
|
return 7;
|
|
}
|
|
|
|
# $_[0]: Address
|
|
# Returns: undef if not detected, (9) if detected.
|
|
# Note: It is already 99% certain this chip exists if we find the PCI
|
|
# entry. The exact address is encoded in PCI space.
|
|
sub sis5595_isa_detect
|
|
{
|
|
my ($addr) = @_;
|
|
my ($key,$adapter,$try,$local_try);
|
|
my $found = 0;
|
|
foreach $local_try (@pci_adapters) {
|
|
if ($local_try->{procid} eq "Silicon Integrated Systems SIS5595") {
|
|
$try = $local_try;
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
return if not $found;
|
|
|
|
$found = 0;
|
|
while ( ($key, $adapter) = each %pci_list) {
|
|
if ((defined($adapter->{vendid}) and
|
|
$try->{vendid} == $adapter->{vendid} and
|
|
$try->{devid} == $adapter->{devid} and
|
|
$try->{func} == $adapter->{func}) or
|
|
(! defined($adapter->{vendid}) and
|
|
$adapter->{desc} =~ /$try->{procid}/ and
|
|
$try->{func} == $adapter->{func})) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
return if not $found;
|
|
|
|
return 9;
|
|
}
|
|
|
|
# $_[0]: Address
|
|
# Returns: undef if not detected, (9) if detected.
|
|
# Note: It is already 99% certain this chip exists if we find the PCI
|
|
# entry. The exact address is encoded in PCI space.
|
|
sub via686a_isa_detect
|
|
{
|
|
my ($addr) = @_;
|
|
my ($key,$adapter,$try,$local_try);
|
|
my $found = 0;
|
|
foreach $local_try (@pci_adapters) {
|
|
if ($local_try->{procid} eq "VIA Technologies VT82C686 Apollo ACPI") {
|
|
$try = $local_try;
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
return if not $found;
|
|
|
|
$found = 0;
|
|
while ( ($key, $adapter) = each %pci_list) {
|
|
if ((defined($adapter->{vendid}) and
|
|
$try->{vendid} == $adapter->{vendid} and
|
|
$try->{devid} == $adapter->{devid} and
|
|
$try->{func} == $adapter->{func}) or
|
|
(! defined($adapter->{vendid}) and
|
|
$adapter->{desc} =~ /$try->{procid}/ and
|
|
$try->{func} == $adapter->{func})) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
return if not $found;
|
|
|
|
return 9;
|
|
}
|
|
|
|
# $_[0]: Address
|
|
# Returns: undef if not detected, (9) if detected.
|
|
# Note: It is already 99% certain this chip exists if we find the PCI
|
|
# entry. The exact address is encoded in PCI space.
|
|
sub via8231_isa_detect
|
|
{
|
|
my ($addr) = @_;
|
|
my ($key,$adapter,$try,$local_try);
|
|
my $found = 0;
|
|
foreach $local_try (@pci_adapters) {
|
|
if ($local_try->{procid} eq "VIA Technologies VT8231 South Bridge") {
|
|
$try = $local_try;
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
return if not $found;
|
|
|
|
$found = 0;
|
|
while ( ($key, $adapter) = each %pci_list) {
|
|
if ((defined($adapter->{vendid}) and
|
|
$try->{vendid} == $adapter->{vendid} and
|
|
$try->{devid} == $adapter->{devid} and
|
|
$try->{func} == $adapter->{func}) or
|
|
(! defined($adapter->{vendid}) and
|
|
$adapter->{desc} =~ /$try->{procid}/ and
|
|
$try->{func} == $adapter->{func})) {
|
|
$found = 1;
|
|
last;
|
|
}
|
|
}
|
|
return if not $found;
|
|
|
|
return 9;
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = ..., 1 = ...)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 8 if detected (tops LM78).
|
|
# Registers used:
|
|
# 0x00: Configuration
|
|
# 0x48: Full I2C Address
|
|
# 0x58: Mfr ID
|
|
# Note that this function is always called through a closure, so the
|
|
# arguments are shifted by one place.
|
|
sub ite_detect
|
|
{
|
|
my $reg;
|
|
my ($chip,$file,$addr) = @_;
|
|
return unless i2c_smbus_read_byte_data($file,0x48) == $addr;
|
|
return unless (i2c_smbus_read_byte_data($file,0x00) & 0x80) == 0x00;
|
|
return unless i2c_smbus_read_byte_data($file,0x58) == 0x90;
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = ..., 1 = ...)
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, 8 if detected (tops LM78).
|
|
# Note: Only address 0x290 is scanned at this moment.
|
|
sub ite_isa_detect
|
|
{
|
|
my ($chip,$addr) = @_ ;
|
|
my $val = inb ($addr + 1);
|
|
return if inb ($addr + 2) != $val or inb ($addr + 3) != $val or
|
|
inb ($addr + 7) != $val;
|
|
|
|
$val = inb($addr + 5) & 0x7f;
|
|
outb($addr+5, ~$val & 0xff);
|
|
if ((inb ($addr+5) & 0x7f) != (~ $val & 0x7f)) {
|
|
outb($addr+5,$val);
|
|
return;
|
|
}
|
|
my $readproc = sub { isa_read_byte $addr + 5, $addr + 6, @_ };
|
|
return unless (&$readproc(0x00) & 0x80) == 0x00;
|
|
my $reg = &$readproc(0x58);
|
|
return unless ($reg == 0x90);
|
|
return 8;
|
|
}
|
|
|
|
|
|
# $_[0]: Chip to detect (0 = ..., 1 = ...)
|
|
# $_[1]: ISA address
|
|
# $_[2]: I2C file handle
|
|
# $_[3]: I2C address
|
|
sub ite_alias_detect
|
|
{
|
|
my ($chip,$isa_addr,$file,$i2c_addr) = @_;
|
|
my $i;
|
|
my $readproc = sub { isa_read_byte $isa_addr + 5, $isa_addr + 6, @_ };
|
|
return 0 unless &$readproc(0x48) == $i2c_addr;
|
|
for ($i = 0x2b; $i <= 0x3d; $i ++) {
|
|
return 0 unless &$readproc($i) == i2c_smbus_read_byte_data($file,$i);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = SPD EEPROM, 1 = Sony Vaio EEPROM,
|
|
# 2 = SPD EEPROM with Software Write Protect)
|
|
# $_[1]: A reference to the file descriptor to access this chip
|
|
# $_[2]: Address
|
|
# Returns: 8 for a memory eeprom (9 if write-protect register found),
|
|
# 4 to 9 for a Sony Vaio eeprom,
|
|
# 1 for an unknown eeprom (2 if write-protect register found)
|
|
# Registers used:
|
|
# 0-63: SPD Data and Checksum
|
|
# 0x80-0x83: Sony Vaio Data ("PCG-")
|
|
# 0xe2, 0xe5, 0xe8, 0xeb, Oxee: Sony Vaio Timestamp constant bytes.
|
|
# 0x1a-0x1c: Sony Vaio MAC address
|
|
# This detection function is a bit tricky; this is to workaround
|
|
# wrong misdetection messages that would else arise.
|
|
sub eeprom_detect
|
|
{
|
|
my ($chip,$file,$addr) = @_;
|
|
my $checksum = 0;
|
|
|
|
# Check the checksum for validity (works for most DIMMs and RIMMs)
|
|
if ($chip != 1) {
|
|
for (my $i = 0; $i <= 62; $i ++) {
|
|
$checksum += i2c_smbus_read_byte_data($file,$i);
|
|
}
|
|
$checksum &= 255;
|
|
$checksum -= i2c_smbus_read_byte_data($file,63);
|
|
}
|
|
if ($chip == 0) {
|
|
if($checksum == 0) {
|
|
return 8;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
if ($chip == 2) {
|
|
# check for 'shadow' write-protect register at 0x30-0x37
|
|
# could be dangerous
|
|
i2c_set_slave_addr($file,$addr - 0x20);
|
|
if(i2c_smbus_write_quick($file,$SMBUS_WRITE) >= 0 &&
|
|
i2c_smbus_read_byte_data($file,0x80) == -1) {
|
|
i2c_set_slave_addr($file,$addr);
|
|
if($checksum == 0) {
|
|
return (9, $addr - 0x20);
|
|
} else {
|
|
return (2, $addr - 0x20);
|
|
}
|
|
}
|
|
i2c_set_slave_addr($file,$addr);
|
|
return;
|
|
}
|
|
|
|
# Look for a Sony Vaio EEPROM ($chip == 1)
|
|
my $vaioconf = 1;
|
|
$vaioconf += 4
|
|
if i2c_smbus_read_byte_data($file,0x80) == 0x50
|
|
&& i2c_smbus_read_byte_data($file,0x81) == 0x43
|
|
&& i2c_smbus_read_byte_data($file,0x82) == 0x47
|
|
&& i2c_smbus_read_byte_data($file,0x83) == 0x2d;
|
|
$vaioconf += 5
|
|
if i2c_smbus_read_byte_data($file,0xe2) == 0x2f
|
|
&& i2c_smbus_read_byte_data($file,0xe5) == 0x2f
|
|
&& i2c_smbus_read_byte_data($file,0xe8) == 0x20
|
|
&& i2c_smbus_read_byte_data($file,0xeb) == 0x3a
|
|
&& i2c_smbus_read_byte_data($file,0xee) == 0x3a;
|
|
$vaioconf += 3
|
|
if i2c_smbus_read_byte_data($file,0x1a) == 0x08
|
|
&& i2c_smbus_read_byte_data($file,0x1b) == 0x00
|
|
&& i2c_smbus_read_byte_data($file,0x1c) == 0x46;
|
|
$vaioconf = 9
|
|
if $vaioconf > 9;
|
|
|
|
if ($vaioconf > 1) {
|
|
return $vaioconf;
|
|
}
|
|
return;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (1) if detected.
|
|
# Registers used:
|
|
# 0x00..0x07: DDC signature
|
|
# 0x08..0x7E: checksumed area
|
|
# 0x7F: checksum
|
|
### commented out additional location checks for now - don't work?
|
|
sub ddcmonitor_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
my $i;
|
|
### for ($i = 0; $i < 8; $i ++) {
|
|
### i2c_set_slave_addr \*FILE,$addr+$i or goto FAILURE;
|
|
i2c_smbus_read_byte_data($file,0x00) == 0x00 or goto FAILURE;
|
|
i2c_smbus_read_byte_data($file,0x01) == 0xFF or goto FAILURE;
|
|
i2c_smbus_read_byte_data($file,0x02) == 0xFF or goto FAILURE;
|
|
i2c_smbus_read_byte_data($file,0x03) == 0xFF or goto FAILURE;
|
|
i2c_smbus_read_byte_data($file,0x04) == 0xFF or goto FAILURE;
|
|
i2c_smbus_read_byte_data($file,0x05) == 0xFF or goto FAILURE;
|
|
i2c_smbus_read_byte_data($file,0x06) == 0xFF or goto FAILURE;
|
|
i2c_smbus_read_byte_data($file,0x07) == 0x00 or goto FAILURE;
|
|
### }
|
|
### i2c_set_slave_addr \*FILE,$addr or return;
|
|
# Check the checksum for validity. We should do this for all addresses,
|
|
# but it would be too slow.
|
|
my $checksum = 0;
|
|
for ($i = 0; $i <= 127; $i = $i + 1) {
|
|
$checksum = $checksum + i2c_smbus_read_byte_data($file,$i);
|
|
}
|
|
$checksum=$checksum & 255;
|
|
if ($checksum != 0) {
|
|
# I have one such monitor...
|
|
return (2,$addr+1,$addr+2,$addr+3,$addr+4,$addr+5,$addr+6,$addr+7);
|
|
}
|
|
return (8,$addr+1,$addr+2,$addr+3,$addr+4,$addr+5,$addr+6,$addr+7);
|
|
FAILURE:
|
|
i2c_set_slave_addr \*FILE,$addr;
|
|
return;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (8) if detected.
|
|
# Registers used:
|
|
# 0x00-0x02: Identification ('P','E','G' -> Pegasus ? :-)
|
|
sub fscpos_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
# check the first 3 registers
|
|
if (i2c_smbus_read_byte_data($file,0x00) != 0x50) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_byte_data($file,0x01) != 0x45) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_byte_data($file,0x02) != 0x47) {
|
|
return;
|
|
}
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (8) if detected.
|
|
# Registers used:
|
|
# 0x00-0x02: Identification ('S','C','Y')
|
|
sub fscscy_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
# check the first 3 registers
|
|
if (i2c_smbus_read_byte_data($file,0x00) != 0x53) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_byte_data($file,0x01) != 0x43) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_byte_data($file,0x02) != 0x59) {
|
|
return;
|
|
}
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (8) if detected.
|
|
# Registers used:
|
|
# 0x00-0x02: Identification ('H','E','R')
|
|
sub fscher_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
# check the first 3 registers
|
|
if (i2c_smbus_read_byte_data($file,0x00) != 0x48) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_byte_data($file,0x01) != 0x45) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_byte_data($file,0x02) != 0x52) {
|
|
return;
|
|
}
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address (unused)
|
|
# Returns: undef if not detected, 5 if detected.
|
|
# Registers used:
|
|
# 0x3E: Manufacturer ID
|
|
# 0x3F: Version/Stepping
|
|
sub lm93_detect
|
|
{
|
|
my $file = shift;
|
|
return unless i2c_smbus_read_byte_data($file, 0x3E) == 0x01
|
|
and i2c_smbus_read_byte_data($file, 0x3F) == 0x73;
|
|
return 5;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, (7) if detected.
|
|
# Registers used:
|
|
# 0x3F: Revision ID
|
|
# 0x48: Address
|
|
# 0x4A, 0x4B, 0x4F, 0x57, 0x58: Reserved bits.
|
|
# We do not use 0x49's reserved bits on purpose. The register is named
|
|
# "VID4/Device ID" so it is doubtful bits 7-1 are really unused.
|
|
sub m5879_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
|
|
return
|
|
unless i2c_smbus_read_byte_data($file, 0x3F) == 0x01;
|
|
|
|
return
|
|
unless i2c_smbus_read_byte_data($file, 0x48) == $addr;
|
|
|
|
return
|
|
unless (i2c_smbus_read_byte_data($file, 0x4A) & 0x06) == 0
|
|
and (i2c_smbus_read_byte_data($file, 0x4B) & 0xFC) == 0
|
|
and (i2c_smbus_read_byte_data($file, 0x4F) & 0xFC) == 0
|
|
and (i2c_smbus_read_byte_data($file, 0x57) & 0xFE) == 0
|
|
and (i2c_smbus_read_byte_data($file, 0x58) & 0xEF) == 0;
|
|
|
|
return (7);
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, 4 or 7 if detected
|
|
# Detection is based on the fact that the SAA1064 has only one readable
|
|
# register, and thus ignores the read address. This register can have value
|
|
# 0x80 (first read since power-up) or 0x00.
|
|
sub saa1064_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
my $status = i2c_smbus_read_byte_data ($file, 0x00);
|
|
|
|
return if ($status & 0x7f) != 0x00;
|
|
|
|
for (my $i=0 ; $i<256; $i++) {
|
|
return if i2c_smbus_read_byte_data ($file, $i) != 0x00;
|
|
}
|
|
|
|
return 7
|
|
if $status == 0x80;
|
|
return 4;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, 1 if detected
|
|
# Detection is rather difficult, since the PCA9540 has a single register.
|
|
# Fortunately, no other device is known to live at this address.
|
|
sub pca9540_detect
|
|
{
|
|
my ($file, $addr) = @_;
|
|
my $reg = i2c_smbus_read_byte($file);
|
|
|
|
return if ($reg & 0xfa);
|
|
return if $reg != i2c_smbus_read_byte($file);
|
|
return if $reg != i2c_smbus_read_byte($file);
|
|
return if $reg != i2c_smbus_read_byte($file);
|
|
|
|
return 1;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: undef if not detected, 3 if detected
|
|
sub max6900_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
my $reg;
|
|
|
|
# SEC
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x81);
|
|
return if
|
|
($reg & 0xF0) > 0x50 or
|
|
($reg & 0x0F) > 9;
|
|
|
|
# MIN
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x83);
|
|
return if
|
|
($reg & 0xF0) > 0x50 or
|
|
($reg & 0x0F) > 9;
|
|
|
|
# HR
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x85);
|
|
return if
|
|
($reg & 0x40) != 0x00 or
|
|
($reg & 0x0F) > 9;
|
|
|
|
# DATE
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x87);
|
|
return if
|
|
$reg == 0x00 or
|
|
($reg & 0xF0) > 0x30 or
|
|
($reg & 0x0F) > 9;
|
|
|
|
# MONTH
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x89);
|
|
return if
|
|
$reg == 0x00 or
|
|
($reg & 0xF0) > 0x10 or
|
|
($reg & 0x0F) > 9;
|
|
|
|
# DAY
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x8B);
|
|
return if
|
|
$reg == 0 or
|
|
$reg > 7;
|
|
|
|
# YEAR
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x8D);
|
|
return if
|
|
($reg & 0xF0) > 0x90 or
|
|
($reg & 0x0F) > 9;
|
|
|
|
# CONTROL
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x8F);
|
|
return if
|
|
($reg & 0x7F) != 0x00;
|
|
|
|
# CENTURY
|
|
$reg = i2c_smbus_read_byte_data ($file, 0x93);
|
|
return if
|
|
($reg & 0xF0) > 0x90 or
|
|
($reg & 0x0F) > 9;
|
|
|
|
return 3;
|
|
}
|
|
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: 1
|
|
# This is a placeholder so we get a report if any device responds
|
|
# to the SMBus Device Default Address (0x61), which is used for
|
|
# ARP in SMBus 2.0.
|
|
sub arp_detect
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
# This checks for non-FFFF values for SpecInfo and Status.
|
|
# The address (0x09) is specified by the SMBus standard so it's likely
|
|
# that this really is a smart battery charger.
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: 5
|
|
sub smartbatt_chgr_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
# check some registers
|
|
if (i2c_smbus_read_word_data($file,0x11) == 0xffff) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_word_data($file,0x13) == 0xffff) {
|
|
return;
|
|
}
|
|
return (5);
|
|
}
|
|
|
|
# This checks for non-FFFF values for State and Info.
|
|
# The address (0x0a) is specified by the SMBus standard so it's likely
|
|
# that this really is a smart battery manager/selector.
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: 5
|
|
sub smartbatt_mgr_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
# check some registers
|
|
if (i2c_smbus_read_word_data($file,0x01) == 0xffff) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_word_data($file,0x04) == 0xffff) {
|
|
return;
|
|
}
|
|
return (5);
|
|
}
|
|
|
|
# This checks for non-FFFF values for temperature, voltage, and current.
|
|
# The address (0x0b) is specified by the SMBus standard so it's likely
|
|
# that this really is a smart battery.
|
|
# $_[0]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[1]: Address
|
|
# Returns: 5
|
|
sub smartbatt_detect
|
|
{
|
|
my ($file,$addr) = @_;
|
|
# check some registers
|
|
if (i2c_smbus_read_word_data($file,0x08) == 0xffff) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_word_data($file,0x09) == 0xffff) {
|
|
return;
|
|
}
|
|
if (i2c_smbus_read_word_data($file,0x0a) == 0xffff) {
|
|
return;
|
|
}
|
|
return (5);
|
|
}
|
|
|
|
# Returns: 4
|
|
# These are simple detectors that only look for a register at the
|
|
# standard location. No writes are performed.
|
|
# For KCS, use the STATUS register. For SMIC, use the FLAGS register.
|
|
sub ipmi_kcs_detect
|
|
{
|
|
return if inb (0x0ca3) == 0xff;
|
|
return (4);
|
|
}
|
|
|
|
sub ipmi_smic_detect
|
|
{
|
|
return if inb (0x0cab) == 0xff;
|
|
return (4);
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = W83L784R/AR, 1 = W83L785R)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 6 or 8 if detected
|
|
# Registers used:
|
|
# 0x40: Configuration
|
|
# 0x4a: Full I2C Address (not W83L785R)
|
|
# 0x4b: I2C addresses of emulated LM75 chips (not W83L785R)
|
|
# 0x4c: Winbond Vendor ID (Low Byte)
|
|
# 0x4d: Winbond Vendor ID (High Byte)
|
|
# 0x4e: Chip ID
|
|
# Note that this function is always called through a closure, so the
|
|
# arguments are shifted by one place.
|
|
sub w83l784r_detect
|
|
{
|
|
my ($reg,@res);
|
|
my ($chip,$file,$addr) = @_;
|
|
|
|
return unless (i2c_smbus_read_byte_data($file,0x40) & 0x80) == 0x00;
|
|
return if $chip == 0
|
|
and i2c_smbus_read_byte_data($file,0x4a) != $addr;
|
|
return unless i2c_smbus_read_byte_data($file,0x4c) == 0xa3;
|
|
return unless i2c_smbus_read_byte_data($file,0x4d) == 0x5c;
|
|
return if $chip == 0
|
|
and i2c_smbus_read_byte_data($file,0x4e) != 0x50;
|
|
return if $chip == 1
|
|
and i2c_smbus_read_byte_data($file,0x4e) != 0x60;
|
|
|
|
$reg = i2c_smbus_read_byte_data($file,0x4b);
|
|
|
|
return 6 if $chip == 1; # W83L785R doesn't have subclients
|
|
|
|
@res = (8);
|
|
push @res, ($reg & 0x07) + 0x48 unless $reg & 0x08 ;
|
|
push @res, (($reg & 0x70) >> 4) + 0x48 unless $reg & 0x80;
|
|
return @res;
|
|
}
|
|
|
|
# $_[0]: Chip to detect (0 = W83L785TS-S)
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 8 if detected
|
|
# Registers used:
|
|
# 0x4C-4E: Mfr and Chip ID
|
|
# Note that this function is always called through a closure, so the
|
|
# arguments are shifted by one place.
|
|
sub w83l785ts_detect
|
|
{
|
|
my ($chip,$file,$addr) = @_;
|
|
return unless i2c_smbus_read_byte_data($file,0x4c) == 0xa3;
|
|
return unless i2c_smbus_read_byte_data($file,0x4d) == 0x5c;
|
|
return unless i2c_smbus_read_byte_data($file,0x4e) == 0x70;
|
|
return (8);
|
|
}
|
|
|
|
# $_[0]: Chip to detect. Always zero for now, but available for future use
|
|
# if somebody finds a way to distinguish MAX6650 and MAX6651.
|
|
# $_[1]: A reference to the file descriptor to access this chip.
|
|
# We may assume an i2c_set_slave_addr was already done.
|
|
# $_[2]: Address
|
|
# Returns: undef if not detected, 4 if detected.
|
|
#
|
|
# The max6650 has no device ID register. However, a few registers have
|
|
# spare bits, which are documented as being always zero on read. We read
|
|
# all of these registers check the spare bits. Any non-zero means this
|
|
# is not a max6650/1.
|
|
#
|
|
# The always zero bits are:
|
|
# configuration byte register (0x02) - top 2 bits
|
|
# gpio status register (0x14) - top 3 bits
|
|
# alarm enable register (0x08) - top 3 bits
|
|
# alarm status register (0x0A) - top 3 bits
|
|
# tachometer count time register (0x16) - top 6 bits
|
|
# Additionally, not all values are possible for lower 3 bits of
|
|
# the configuration register.
|
|
sub max6650_detect
|
|
{
|
|
my ($chip, $file) = @_;
|
|
|
|
my $conf = i2c_smbus_read_byte_data($file,0x02);
|
|
|
|
return if i2c_smbus_read_byte_data($file,0x16) & 0xFC;
|
|
return if i2c_smbus_read_byte_data($file,0x0A) & 0xE0;
|
|
return if i2c_smbus_read_byte_data($file,0x08) & 0xE0;
|
|
return if i2c_smbus_read_byte_data($file,0x14) & 0xE0;
|
|
return if ($conf & 0xC0) or ($conf & 0x07) > 4;
|
|
|
|
return 4;
|
|
}
|
|
|
|
|
|
################
|
|
# 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' (%s)\n", $data->{i2c_adap}, $data->{i2c_algo};
|
|
printf " Busdriver `%s', I2C address 0x%02x",
|
|
$data->{i2c_driver}, $data->{i2c_addr};
|
|
if (exists $data->{i2c_sub_addrs}) {
|
|
print " (and";
|
|
my $sub_addr;
|
|
foreach $sub_addr (@{$data->{i2c_sub_addrs}}) {
|
|
printf " 0x%02x",$sub_addr;
|
|
}
|
|
print ")"
|
|
}
|
|
print "\n";
|
|
}
|
|
if ($is_isa) {
|
|
print " " if $is_i2c;
|
|
if ($data->{isa_addr}) {
|
|
printf "ISA bus address 0x%04x (Busdriver `i2c-isa')\n",
|
|
$data->{isa_addr};
|
|
} else {
|
|
printf "ISA bus, undetermined address (Busdriver `i2c-isa')\n"
|
|
}
|
|
}
|
|
printf " Chip `%s' (confidence: %d)\n",
|
|
$data->{chipname}, $data->{conf};
|
|
}
|
|
}
|
|
|
|
# $_[0]: 1 if ISA bus is prefered, 0 for SMBus
|
|
# We build here an array adapters, indexed on the number the adapter has
|
|
# at this moment (we assume only loaded adapters are interesting at all;
|
|
# everything that got scanned also got loaded). Each entry is a reference
|
|
# to a hash containing:
|
|
# driver: Name of the adapter driver
|
|
# nr_now: Number of the bus now
|
|
# nr_later: Number of the bus when the modprobes are done (not included if the
|
|
# driver should not be loaded)
|
|
# A second array, called
|
|
sub generate_modprobes
|
|
{
|
|
my ($prefer_isa) = @_;
|
|
|
|
my ($chip,$detection,$nr,$i,@optionlist,@probelist,$driver,$isa,$adap);
|
|
my @adapters;
|
|
my $modprobes = "";
|
|
my $configfile = "";
|
|
|
|
# These are always needed
|
|
$configfile .= "# I2C module options\n";
|
|
$configfile .= "alias char-major-89 i2c-dev\n";
|
|
|
|
# Collect all loaded adapters
|
|
# i2cdetect -l either cats /proc/bus/i2c or scans sysfs for the same information
|
|
open INPUTFILE,"i2cdetect -l |" or die "Couldn't find i2cdetect program!!";
|
|
while (<INPUTFILE>) {
|
|
my ($dev_nr,$type,$adap,$algo) = /^i2c-(\S+)\s+(\S+)\s+(.*?)\s*\t\s*(.*?)\s+$/;
|
|
next if ($type eq "dummy");
|
|
$adapters[$dev_nr]->{driver} = find_adapter_driver($adap,$algo);
|
|
$adapters[$dev_nr]->{adapname} = $adap;
|
|
$adapters[$dev_nr]->{algoname} = $algo;
|
|
}
|
|
close INPUTFILE;
|
|
|
|
# Collect all adapters used
|
|
$nr = 0;
|
|
$isa = 0;
|
|
$modprobes .= "# I2C adapter drivers\n";
|
|
foreach $chip (@chips_detected) {
|
|
foreach $detection (@{$chip->{detected}}) {
|
|
# If there is more than one bus detected by a driver, they are
|
|
# still all added. So we number them in the correct order
|
|
if (exists $detection->{i2c_driver} and
|
|
not exists $adapters[$detection->{i2c_devnr}]->{nr_later} and
|
|
not (exists $detection->{isa_addr} and $prefer_isa)) {
|
|
foreach $adap (@adapters) {
|
|
next unless exists $adap->{driver};
|
|
$adap->{nr_later} = $nr++ if $adap->{driver} eq $detection->{i2c_driver};
|
|
}
|
|
}
|
|
if (exists $detection->{isa_addr} and
|
|
not (exists $detection->{i2c_driver} and not $prefer_isa)) {
|
|
$isa=1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for ($i = 0; $i < $nr; $i++) {
|
|
foreach $adap (@adapters) {
|
|
next unless exists $adap->{nr_later} and $adap->{nr_later} == $i;
|
|
if ($adap->{driver} eq "UNKNOWN") {
|
|
$modprobes .= "# modprobe unknown adapter ".$adap->{adapname}."\n";
|
|
} elsif ($adap->{driver} eq "DISABLED") {
|
|
$modprobes .= "# modprobe disabled adapter ".$adap->{adapname}."\n";
|
|
} elsif ($adap->{driver} eq "to-be-written") {
|
|
$modprobes .= "# no driver available for adapter ".$adap->{adapname}."\n";
|
|
} else {
|
|
$modprobes .= "modprobe $adap->{driver}\n"
|
|
unless $modprobes =~ /modprobe $adap->{driver}\n/;
|
|
}
|
|
last;
|
|
}
|
|
}
|
|
$modprobes .= "modprobe i2c-isa\n" if ($isa);
|
|
|
|
# Now determine the chip probe lines
|
|
$modprobes .= "# I2C chip drivers\n";
|
|
foreach $chip (@chips_detected) {
|
|
next if not @{$chip->{detected}};
|
|
if ($chip->{driver} eq "to-be-written") {
|
|
$modprobes .= "# no driver for $chip->{detected}[0]{chipname} yet\n";
|
|
} else {
|
|
$modprobes .= "modprobe $chip->{driver}\n";
|
|
}
|
|
@optionlist = ();
|
|
@probelist = ();
|
|
|
|
# Handle detects at addresses normally not probed
|
|
foreach $detection (@{$chip->{detected}}) {
|
|
push @probelist, $adapters[$detection->{i2c_devnr}]->{nr_later},
|
|
$detection->{i2c_addr}
|
|
if exists $detection->{i2c_addr} and
|
|
exists $detection->{i2c_extra};
|
|
push @probelist, -1, $detection->{isa_addr}
|
|
if exists $detection->{isa_addr} and
|
|
exists $detection->{isa_extra};
|
|
}
|
|
|
|
# Handle misdetects
|
|
foreach $detection (@{$chip->{misdetected}}) {
|
|
push @optionlist, $adapters[$detection->{i2c_devnr}]->{nr_later},
|
|
$detection->{i2c_addr}
|
|
if exists $detection->{i2c_addr} and
|
|
exists $adapters[$detection->{i2c_devnr}]->{nr_later};
|
|
push @optionlist, -1, $detection->{isa_addr}
|
|
if exists $detection->{isa_addr} and $isa;
|
|
}
|
|
|
|
# Handle aliases
|
|
foreach $detection (@{$chip->{detected}}) {
|
|
if (exists $detection->{i2c_driver} and
|
|
exists $detection->{isa_addr} and
|
|
exists $adapters[$detection->{i2c_devnr}]->{nr_later} and
|
|
$isa) {
|
|
if ($prefer_isa) {
|
|
push @optionlist,$adapters[$detection->{i2c_devnr}]->{nr_later},
|
|
$detection->{i2c_addr};
|
|
} else {
|
|
push @optionlist, -1, $detection->{isa_addr}
|
|
}
|
|
}
|
|
}
|
|
|
|
next if not (@probelist or @optionlist);
|
|
$configfile .= "options $chip->{driver}";
|
|
$configfile .= sprintf " ignore=%d,0x%02x",shift @optionlist,
|
|
shift @optionlist
|
|
if @optionlist;
|
|
$configfile .= sprintf ",%d,0x%02x",shift @optionlist, shift @optionlist
|
|
while @optionlist;
|
|
$configfile .= sprintf " probe=%d,0x%02x",shift @probelist,
|
|
shift @probelist
|
|
if @probelist;
|
|
$configfile .= sprintf ",%d,0x%02x",shift @probelist, shift @probelist
|
|
while @probelist;
|
|
$configfile .= "\n";
|
|
}
|
|
|
|
return ($modprobes,$configfile);
|
|
|
|
}
|
|
|
|
# returns:
|
|
# 0, could not determine (can't read /dev/mem...)
|
|
# 1, safe system (VPD record not found)
|
|
# bios string (e.g. "INET32WW"), unsafe system
|
|
# VPD is documented here:
|
|
# http://www.pc.ibm.com/qtechinfo/MIGR-45120.html
|
|
sub vpd_bios_build_id
|
|
{
|
|
my $pos = 0xF0000;
|
|
my $found = 0;
|
|
my $bbid;
|
|
|
|
return 0
|
|
unless open MEM, '/dev/mem';
|
|
binmode MEM;
|
|
unless (seek MEM, $pos, SEEK_SET)
|
|
{
|
|
close MEM;
|
|
return 0;
|
|
}
|
|
while ($pos <= 0xFFFD0 && !$found)
|
|
{
|
|
my $r = read(MEM, my $buf, 48);
|
|
unless ($r == 48)
|
|
{
|
|
close MEM;
|
|
return 0;
|
|
}
|
|
seek (MEM, -32, SEEK_CUR);
|
|
my $len;
|
|
if (substr($buf, 0, 5) eq "\xAA\x55VPD"
|
|
&& ord(substr($buf, 5, 1)) >= 0x30)
|
|
{
|
|
if (unpack('%8C*', substr($buf, 0x0D, 0x30-0x0D)) != 0)
|
|
{
|
|
printf " Bad VPD checksum (0x%02X)! Please report.\n",
|
|
ord(substr($buf, 0x2F, 1));
|
|
}
|
|
$bbid = substr($buf, 13, 9);
|
|
$bbid =~ s/[\x00 ]*$//; # right trim
|
|
$found++;
|
|
}
|
|
$pos += 16;
|
|
}
|
|
close MEM;
|
|
|
|
return 1 unless $found;
|
|
print " System vendor: IBM\n";
|
|
print " BIOS version: $bbid\n";
|
|
return "$bbid";
|
|
}
|
|
|
|
# returns:
|
|
# 1 : the system is known to be safe
|
|
# 0 : the system safeness is unknown
|
|
# If the system is know to be unsafe (i.e. for now, IBM systems), never
|
|
# return.
|
|
sub safe_system_vendor
|
|
{
|
|
if ($> != 0)
|
|
{
|
|
print " As you are not root, we can't determine your system vendor.\n";
|
|
return 0;
|
|
}
|
|
|
|
my $vpd_bbid = vpd_bios_build_id();
|
|
|
|
return 0 if $vpd_bbid eq '0';
|
|
return 1 if $vpd_bbid eq '1';
|
|
|
|
print "Sorry, we won't let you go on. IBM systems are known to have\n".
|
|
"serious problems with lm_sensors, resulting in hardware failures.\n".
|
|
"For more information, see README.thinkpad or\n".
|
|
"http://www2.lm-sensors.nu/~lm78/cvs/lm_sensors2/README.thinkpad.\n\n".
|
|
"We will be progressively updating our user-space tools so as to prevent\n".
|
|
"the problem from occuring. Some kernel drivers will need updates too.\n".
|
|
"This lock will be removed once everything is fixed and believed to be\n".
|
|
"safe.\n\n";
|
|
|
|
exit;
|
|
}
|
|
|
|
sub main
|
|
{
|
|
my (@adapters,$res,$did_adapter_detection,$adapter);
|
|
|
|
initialize_conf;
|
|
initialize_proc_pci;
|
|
initialize_modules_list;
|
|
initialize_kernel_version;
|
|
|
|
print "\nThis program will help you determine which I2C/SMBus modules you need to\n",
|
|
"load to use lm_sensors most effectively. You need to have i2c and\n",
|
|
"lm_sensors installed before running this program.\n";
|
|
print "Also, you need to be `root', or at least have access to the $dev_i2c*\n",
|
|
"files, for most things.\n";
|
|
print "If you have patched your kernel and have some drivers built in, you can\n",
|
|
"safely answer NO if asked to load some modules. In this case, things may\n",
|
|
"seem a bit confusing, but they will still work.\n\n";
|
|
print "It is generally safe and recommended to accept the default answers to all\n",
|
|
"questions, unless you know what you're doing.\n\n";
|
|
|
|
unless (safe_system_vendor ())
|
|
{
|
|
print " IF THIS IS AN IBM THINKPAD, PRESS CTRL-C NOW!\n";
|
|
print " IBM Thinkpads have a severely broken i2c/SMBus implementation, ";
|
|
print "just scanning\n";
|
|
print " the bus will break your Thinkpad forever!\n";
|
|
print " If this is a non-Thinkpad IBM, we still suggest you press CTRL+C. We have\n";
|
|
print " had users reporting system breakage on other IBM systems as well.\n\n";
|
|
}
|
|
|
|
print " We can start with probing for (PCI) I2C or SMBus adapters.\n";
|
|
print " You do not need any special privileges for this.\n";
|
|
print " Do you want to probe now? (YES/no): ";
|
|
@adapters = adapter_pci_detection
|
|
if ($did_adapter_detection = not <STDIN> =~ /\s*[Nn]/);
|
|
|
|
print "\n";
|
|
|
|
if (not $did_adapter_detection) {
|
|
print "As you skipped adapter detection, we will only scan already loaded\n".
|
|
"adapter modules.\n";
|
|
} elsif ($> != 0) {
|
|
print "As you are not root, we can't load adapter modules. We will only scan\n".
|
|
"already loaded adapters.\n";
|
|
} else {
|
|
print "We will now try to load each adapter module in turn.\n";
|
|
foreach $adapter (@adapters) {
|
|
next if $adapter eq "DISABLED";
|
|
if (exists($modules_list{$adapter})) {
|
|
print "Module `$adapter' already loaded.\n";
|
|
} else {
|
|
print "Load `$adapter' (say NO if built into your kernel)? (YES/no): ";
|
|
unless (<STDIN> =~ /^\s*[Nn]/) {
|
|
if (system ("modprobe", $adapter)) {
|
|
print "Loading failed... skipping.\n";
|
|
if ($adapter eq "rivatv") {
|
|
print "** Note: rivatv module is available at http://rivatv.sourceforge.net/\n";
|
|
}
|
|
} else {
|
|
print "Module loaded succesfully.\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
print "If you have undetectable or unsupported adapters, you can have them\n".
|
|
"scanned by manually loading the modules before running this script.\n\n";
|
|
|
|
print " To continue, we need module `i2c-dev' to be loaded.\n";
|
|
print " If it is built-in into your kernel, you can safely skip this.\n";
|
|
if (exists($modules_list{"i2c-dev"})) {
|
|
print "i2c-dev is already loaded.\n";
|
|
} else {
|
|
if ($> != 0) {
|
|
print " i2c-dev is not loaded. As you are not root, we will just hope ",
|
|
"you edited\n",
|
|
" `$modules_conf' for automatic loading of\n",
|
|
" this module. If not, you won't be able to open any $dev_i2c* file.\n";
|
|
} else {
|
|
print " i2c-dev is not loaded. Do you want to load it now? (YES/no): ";
|
|
if (<STDIN> =~ /^\s*[Nn]/) {
|
|
print " Well, you will know best. We will just hope you edited ",
|
|
"`$modules_conf'\n",
|
|
" for automatic loading of this module. If not,\n",
|
|
" you won't be able to open any $dev_i2c* file (unless you",
|
|
"have it built-in\n",
|
|
" into your kernel)\n";
|
|
} elsif (system "modprobe","i2c-dev") {
|
|
print " Loading failed, expect problems later on.\n";
|
|
} else {
|
|
print " Module loaded succesfully.\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
print "\n We are now going to do the adapter probings. Some adapters may ",
|
|
"hang halfway\n",
|
|
" through; we can't really help that. Also, some chips will be double ",
|
|
"detected;\n",
|
|
" we choose the one with the highest confidence value in that case.\n",
|
|
" If you found that the adapter hung after probing a certain address, ",
|
|
"you can\n",
|
|
" specify that address to remain unprobed. That ",
|
|
"often\n",
|
|
" includes address 0x69 (clock chip).\n";
|
|
|
|
my ($inp,@not_to_scan,$inp2);
|
|
# i2cdetect -l either cats /proc/bus/i2c or scans sysfs for the same information
|
|
open INPUTFILE,"i2cdetect -l |" or die "Couldn't find i2cdetect program!!";
|
|
while (<INPUTFILE>) {
|
|
my ($dev_nr,$type,$adap,$algo) = /^i2c-(\S+)\s+(\S+)\s+(.*?)\s*\t\s*(.*?)\s+$/;
|
|
next if ($type eq "dummy");
|
|
print "\n";
|
|
print "Next adapter: $adap ($algo)\n";
|
|
print "Do you want to scan it? (YES/no/selectively): ";
|
|
|
|
$inp = <STDIN>;
|
|
@not_to_scan=();
|
|
if ($inp =~ /^\s*[Ss]/) {
|
|
print "Please enter one or more addresses not to scan. Separate them ",
|
|
"with comma's.\n",
|
|
"You can specify a range by using dashes. Addresses may be ",
|
|
"decimal (like 54)\n",
|
|
"or hexadecimal (like 0x33).\n",
|
|
"Addresses: ";
|
|
$inp2 = <STDIN>;
|
|
chop $inp2;
|
|
@not_to_scan = parse_not_to_scan 0,0x7f,$inp2;
|
|
}
|
|
scan_adapter $dev_nr, $adap, $algo, find_adapter_driver($adap,$algo),
|
|
\@not_to_scan unless $inp =~ /^\s*[Nn]/;
|
|
}
|
|
|
|
print "\nSome chips are also accessible through the ISA bus. ISA probes are\n".
|
|
"typically a bit more dangerous, as we have to write to I/O ports to do\n".
|
|
"this. This is usually safe though.\n\n";
|
|
if ($> != 0) {
|
|
print "As you are not root, we shall skip this step.\n";
|
|
} else {
|
|
print "Do you want to scan the ISA bus? (YES/no): ";
|
|
unless (<STDIN> =~ /^\s*n/i) {
|
|
initialize_ioports();
|
|
scan_isa_bus();
|
|
close_ioports();
|
|
}
|
|
}
|
|
|
|
print "\nSome Super I/O chips may also contain sensors. Super I/O probes are\n".
|
|
"typically a bit more dangerous, as we have to write to I/O ports to do\n".
|
|
"this. This is usually safe though.\n\n";
|
|
if ($> != 0) {
|
|
print "As you are not root, we shall skip this step.\n";
|
|
} else {
|
|
print "Do you want to scan for Super I/O sensors? (YES/no): ";
|
|
unless (<STDIN> =~ /^\s*n/i) {
|
|
initialize_ioports();
|
|
scan_superio(0x2e, 0x2f);
|
|
print "\nDo you want to scan for secondary Super I/O sensors? (YES/no): ";
|
|
unless (<STDIN> =~ /^\s*n/i) {
|
|
scan_superio(0x4e, 0x4f);
|
|
}
|
|
close_ioports();
|
|
}
|
|
}
|
|
|
|
if(! @chips_detected) {
|
|
print "\n Sorry, no chips were detected.\n",
|
|
" Either your sensors are not supported, or they are\n",
|
|
" connected to an I2C bus adapter that we do not support.\n",
|
|
" See doc/FAQ, doc/lm_sensors-FAQ.html, or\n",
|
|
" http://www2.lm-sensors.nu/~lm78/cvs/lm_sensors2/doc/lm_sensors-FAQ.html\n",
|
|
" (FAQ #4.24.3) for further information.\n",
|
|
" If you find out what chips are on your board, see\n",
|
|
" http://secure.netroedge.com/~lm78/newdrivers.html for driver status.\n";
|
|
exit;
|
|
}
|
|
|
|
print "\n Now follows a summary of the probes I have just done.\n";
|
|
print " Just press ENTER to continue: ";
|
|
<STDIN>;
|
|
|
|
my ($chip,$data);
|
|
foreach $chip (@chips_detected) {
|
|
print "\nDriver `$$chip{driver}' ";
|
|
if (@{$$chip{detected}}) {
|
|
if (@{$$chip{misdetected}}) {
|
|
print "(should be inserted but causes problems):\n";
|
|
} else {
|
|
print "(should be inserted):\n";
|
|
}
|
|
} else {
|
|
if (@{$$chip{misdetected}}) {
|
|
print "(may not be inserted):\n";
|
|
} else {
|
|
print "(should not be inserted, but is harmless):\n";
|
|
}
|
|
}
|
|
if (@{$$chip{detected}}) {
|
|
print " Detects correctly:\n";
|
|
print_chips_report $chip->{detected};
|
|
}
|
|
if (@{$$chip{misdetected}}) {
|
|
print " Misdetects:\n";
|
|
print_chips_report $chip->{misdetected};
|
|
}
|
|
}
|
|
|
|
print "\n\n",
|
|
" I will now generate the commands needed to load the I2C modules.\n",
|
|
" Sometimes, a chip is available both through the ISA bus and an ",
|
|
"I2C bus.\n",
|
|
" ISA bus access is faster, but you need to load an additional driver ",
|
|
"module\n",
|
|
" for it. If you have the choice, do you want to use the ISA bus or ",
|
|
"the\n",
|
|
" I2C/SMBus (ISA/smbus)? ";
|
|
my $use_isa = not <STDIN> =~ /\s*[Ss]/;
|
|
|
|
my ($modprobes,$configfile) = generate_modprobes $use_isa;
|
|
print "\nTo make the sensors modules behave correctly, add these lines to\n",
|
|
"$modules_conf:\n\n";
|
|
print "#----cut here----\n";
|
|
print $configfile;
|
|
print "#----cut here----\n";
|
|
print "\nTo load everything that is needed, add this to some /etc/rc* ",
|
|
"file:\n\n";
|
|
print "#----cut here----\n";
|
|
print $modprobes;
|
|
print "# sleep 2 # optional\n",
|
|
"/usr/local/bin/sensors -s # recommended\n";
|
|
print "#----cut here----\n";
|
|
print "\nWARNING! If you have some things built into your kernel, the list above\n",
|
|
"will contain too many modules. Skip the appropriate ones! You really should\n",
|
|
"try these commands right now to make sure everything is working properly.\n",
|
|
"Monitoring programs won't work until it's done.\n";
|
|
|
|
my $have_sysconfig = -d '/etc/sysconfig';
|
|
print "\nDo you want to generate /etc/sysconfig/lm_sensors? (".
|
|
($have_sysconfig?"YES/no":"yes/NO")."): ";
|
|
if ($> != 0) {
|
|
print "\nAs you are not root, we shall skip this step.\n";
|
|
} else {
|
|
$_ = <STDIN>;
|
|
if (($have_sysconfig and not m/^\s*[Nn]/) or m/^\s*[Yy]/) {
|
|
unless ($have_sysconfig) {
|
|
mkdir '/etc/sysconfig', 0777
|
|
or die "Sorry, can't create /etc/sysconfig ($!)?!?";
|
|
}
|
|
open(SYSCONFIG, ">/etc/sysconfig/lm_sensors")
|
|
or die "Sorry, can't create /etc/sysconfig/lm_sensors ($!)?!?";
|
|
print SYSCONFIG <<'EOT';
|
|
# /etc/sysconfig/sensors - Defines modules loaded by /etc/rc.d/init.d/lm_sensors
|
|
# Copyright (c) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
#
|
|
#
|
|
# See also the lm_sensors homepage at:
|
|
# http://www2.lm-sensors.nu/~lm78/index.html
|
|
#
|
|
# This file is used by /etc/rc.d/init.d/lm_sensors and defines the modules to
|
|
# be loaded/unloaded. This file is sourced into /etc/rc.d/init.d/lm_sensors.
|
|
#
|
|
# The format of this file is a shell script that simply defines the modules
|
|
# in order as normal variables with the special names:
|
|
# MODULE_0, MODULE_1, MODULE_2, etc.
|
|
#
|
|
# List the modules that are to be loaded for your system
|
|
#
|
|
EOT
|
|
print SYSCONFIG
|
|
"# Generated by sensors-detect on " . scalar localtime() . "\n";
|
|
my @modules = grep /^modprobe /, split "\n", $modprobes;
|
|
my $i = 0;
|
|
my $sysconfig = "";
|
|
foreach (@modules) {
|
|
s/^modprobe //;
|
|
$sysconfig .= "MODULE_$i=$_\n";
|
|
$i++;
|
|
}
|
|
print SYSCONFIG $sysconfig;
|
|
close(SYSCONFIG);
|
|
print "Copy prog/init/lm_sensors.init to /etc/rc.d/init.d/lm_sensors\n";
|
|
print "for initialization at boot time.\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
main;
|