// SPDX-License-Identifier: GPL-2.0-or-later /* * sis5595.c - Part of lm_sensors, Linux kernel modules * for hardware monitoring * * Copyright (C) 1998 - 2001 Frodo Looijaard <frodol@dds.nl>, * Kyösti Mälkki <kmalkki@cc.hut.fi>, and * Mark D. Studebaker <mdsxyz123@yahoo.com> * Ported to Linux 2.6 by Aurelien Jarno <aurelien@aurel32.net> with * the help of Jean Delvare <jdelvare@suse.de>
*/
/* * SiS southbridge has a LM78-like chip integrated on the same IC. * This driver is a customized copy of lm78.c * * Supports following revisions: * Version PCI ID PCI Revision * 1 1039/0008 AF or less * 2 1039/0008 B0 or greater * * Note: these chips contain a 0008 device which is incompatible with the * 5595. We recognize these by the presence of the listed * "blacklist" PCI ID and refuse to load. * * NOT SUPPORTED PCI ID BLACKLIST PCI ID * 540 0008 0540 * 550 0008 0550 * 5513 0008 5511 * 5581 0008 5597 * 5582 0008 5597 * 5597 0008 5597 * 5598 0008 5597/5598 * 630 0008 0630 * 645 0008 0645 * 730 0008 0730 * 735 0008 0735
*/
/* * If force_addr is set to anything different from 0, we forcibly enable * the device at the given address.
*/ static u16 force_addr;
module_param(force_addr, ushort, 0);
MODULE_PARM_DESC(force_addr, "Initialize the base address of the sensors");
/* * On the first version of the chip, the temp registers are separate. * On the second version, * TEMP pin is shared with IN4, configured in PCI register 0x7A. * The registers are the same as well. * OVER and HYST are really MAX and MIN.
*/
/* * FAN DIV: 1, 2, 4, or 8 * REG: 0, 1, 2, or 3 (respectively)
*/ #define DIV_FROM_REG(val) (1 << (val))
/* * For each registered chip, we need to keep some data in memory. * The structure is dynamically allocated.
*/ struct sis5595_data { unsignedshort addr; constchar *name; struct device *hwmon_dev; struct mutex lock;
struct mutex update_lock; bool valid; /* true if following fields are valid */ unsignedlong last_updated; /* In jiffies */ char maxins; /* == 3 if temp enabled, otherwise == 4 */
u8 revision; /* Reg. value */
u8 in[5]; /* Register value */
u8 in_max[5]; /* Register value */
u8 in_min[5]; /* Register value */
u8 fan[2]; /* Register value */
u8 fan_min[2]; /* Register value */
s8 temp; /* Register value */
s8 temp_over; /* Register value */
s8 temp_hyst; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u16 alarms; /* Register encoding, combined */
};
staticstruct pci_dev *s_bridge; /* pointer to the (only) sis5595 */
/* ISA access must be locked explicitly. */ staticint sis5595_read_value(struct sis5595_data *data, u8 reg)
{ int res;
/* * Note: we save and restore the fan minimum here, because its value is * determined in part by the fan divisor. This follows the principle of * least surprise; the user doesn't expect the fan minimum to change just * because the divisor changed.
*/ static ssize_t fan_div_store(struct device *dev, struct device_attribute *da, constchar *buf, size_t count)
{ struct sis5595_data *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(da); int nr = attr->index; unsignedlong min; int reg; unsignedlong val; int err;
err = kstrtoul(buf, 10, &val); if (err) return err;
mutex_lock(&data->update_lock);
min = FAN_FROM_REG(data->fan_min[nr],
DIV_FROM_REG(data->fan_div[nr]));
reg = sis5595_read_value(data, SIS5595_REG_FANDIV);
switch (val) { case 1:
data->fan_div[nr] = 0; break; case 2:
data->fan_div[nr] = 1; break; case 4:
data->fan_div[nr] = 2; break; case 8:
data->fan_div[nr] = 3; break; default:
dev_err(dev, "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n",
val);
mutex_unlock(&data->update_lock); return -EINVAL;
}
/* Called when we have found a new SIS5595. */ staticvoid sis5595_init_device(struct sis5595_data *data)
{
u8 config = sis5595_read_value(data, SIS5595_REG_CONFIG); if (!(config & 0x01))
sis5595_write_value(data, SIS5595_REG_CONFIG,
(config & 0xf7) | 0x01);
}
/* This is called when the module is loaded */ staticint sis5595_probe(struct platform_device *pdev)
{ int err = 0; int i; struct sis5595_data *data; struct resource *res; char val;
/* Reserve the ISA region */
res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, SIS5595_EXTENT,
DRIVER_NAME)) return -EBUSY;
data = devm_kzalloc(&pdev->dev, sizeof(struct sis5595_data),
GFP_KERNEL); if (!data) return -ENOMEM;
staticint blacklist[] = {
PCI_DEVICE_ID_SI_540,
PCI_DEVICE_ID_SI_550,
PCI_DEVICE_ID_SI_630,
PCI_DEVICE_ID_SI_645,
PCI_DEVICE_ID_SI_730,
PCI_DEVICE_ID_SI_735,
PCI_DEVICE_ID_SI_5511, /* * 5513 chip has the 0008 device but * that ID shows up in other chips so we * use the 5511 ID for recognition
*/
PCI_DEVICE_ID_SI_5597,
PCI_DEVICE_ID_SI_5598,
0 };
for (i = blacklist; *i != 0; i++) { struct pci_dev *d;
d = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); if (d) {
dev_err(&d->dev, "Looked for SIS5595 but found unsupported device %.4x\n",
*i);
pci_dev_put(d); return -ENODEV;
}
}
force_addr &= ~(SIS5595_EXTENT - 1); if (force_addr) {
dev_warn(&dev->dev, "Forcing ISA address 0x%x\n", force_addr);
pci_write_config_word(dev, SIS5595_BASE_REG, force_addr);
}
err = pci_read_config_word(dev, SIS5595_BASE_REG, &address); if (err != PCIBIOS_SUCCESSFUL) {
dev_err(&dev->dev, "Failed to read ISA address\n"); return -ENODEV;
}
address &= ~(SIS5595_EXTENT - 1); if (!address) {
dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n"); return -ENODEV;
} if (force_addr && address != force_addr) { /* doesn't work for some chips? */
dev_err(&dev->dev, "Failed to force ISA address\n"); return -ENODEV;
}
err = pci_read_config_byte(dev, SIS5595_ENABLE_REG, &enable); if (err != PCIBIOS_SUCCESSFUL) {
dev_err(&dev->dev, "Failed to read enable register\n"); return -ENODEV;
} if (!(enable & 0x80)) {
err = pci_write_config_byte(dev, SIS5595_ENABLE_REG, enable | 0x80); if (err != PCIBIOS_SUCCESSFUL) goto enable_fail;
/* doesn't work for some chips! */ if (!(enable & 0x80)) goto enable_fail;
}
if (platform_driver_register(&sis5595_driver)) {
dev_dbg(&dev->dev, "Failed to register sis5595 driver\n"); gotoexit;
}
s_bridge = pci_dev_get(dev); /* Sets global pdev as a side effect */ if (sis5595_device_add(address)) goto exit_unregister;
/* * Always return failure here. This is to allow other drivers to bind * to this pci device. We don't really want to have control over the * pci device, we only wanted to read as few register values from it.
*/ return -ENODEV;
enable_fail:
dev_err(&dev->dev, "Failed to enable HWM device\n"); gotoexit;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.