// SPDX-License-Identifier: GPL-2.0-or-later /* * vt1211.c - driver for the VIA VT1211 Super-I/O chip integrated hardware * monitoring features * Copyright (C) 2006 Juerg Haefliger <juergh@gmail.com> * * This driver is based on the driver for kernel 2.4 by Mark D. Studebaker * and its port to kernel 2.6 by Lars Ekman.
*/
/* --------------------------------------------------------------------- * Super-I/O constants and functions
* --------------------------------------------------------------------- */
/* * Configuration index port registers * The vt1211 can live at 2 different addresses so we need to probe both
*/ #define SIO_REG_CIP1 0x2e #define SIO_REG_CIP2 0x4e
/* Configuration registers */ #define SIO_VT1211_LDN 0x07 /* logical device number */ #define SIO_VT1211_DEVID 0x20 /* device ID */ #define SIO_VT1211_DEVREV 0x21 /* device revision */ #define SIO_VT1211_ACTIVE 0x30 /* HW monitor active */ #define SIO_VT1211_BADDR 0x60 /* base I/O address */ #define SIO_VT1211_ID 0x3c /* VT1211 device ID */
static ssize_t show_in(struct device *dev, struct device_attribute *attr, char *buf)
{ struct vt1211_data *data = vt1211_update_device(dev); struct sensor_device_attribute_2 *sensor_attr_2 =
to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; int res;
switch (fn) { case SHOW_IN_INPUT:
res = IN_FROM_REG(ix, data->in[ix]); break; case SHOW_SET_IN_MIN:
res = IN_FROM_REG(ix, data->in_min[ix]); break; case SHOW_SET_IN_MAX:
res = IN_FROM_REG(ix, data->in_max[ix]); break; case SHOW_IN_ALARM:
res = (data->alarms >> bitalarmin[ix]) & 1; break; default:
res = 0;
dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
}
return sprintf(buf, "%d\n", res);
}
static ssize_t set_in(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct vt1211_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sensor_attr_2 =
to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; long val; int err;
err = kstrtol(buf, 10, &val); if (err) return err;
static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf)
{ struct vt1211_data *data = vt1211_update_device(dev); struct sensor_device_attribute_2 *sensor_attr_2 =
to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; int res;
switch (fn) { case SHOW_TEMP_INPUT:
res = TEMP_FROM_REG(ix, data->temp[ix]); break; case SHOW_SET_TEMP_MAX:
res = TEMP_FROM_REG(ix, data->temp_max[ix]); break; case SHOW_SET_TEMP_MAX_HYST:
res = TEMP_FROM_REG(ix, data->temp_hyst[ix]); break; case SHOW_TEMP_ALARM:
res = (data->alarms >> bitalarmtemp[ix]) & 1; break; default:
res = 0;
dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
}
return sprintf(buf, "%d\n", res);
}
static ssize_t set_temp(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct vt1211_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sensor_attr_2 =
to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; long val; int err;
err = kstrtol(buf, 10, &val); if (err) return err;
static ssize_t show_fan(struct device *dev, struct device_attribute *attr, char *buf)
{ struct vt1211_data *data = vt1211_update_device(dev); struct sensor_device_attribute_2 *sensor_attr_2 =
to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; int res;
switch (fn) { case SHOW_FAN_INPUT:
res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]); break; case SHOW_SET_FAN_MIN:
res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]); break; case SHOW_SET_FAN_DIV:
res = DIV_FROM_REG(data->fan_div[ix]); break; case SHOW_FAN_ALARM:
res = (data->alarms >> bitalarmfan[ix]) & 1; break; default:
res = 0;
dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
}
return sprintf(buf, "%d\n", res);
}
static ssize_t set_fan(struct device *dev, struct device_attribute *attr, constchar *buf, size_t count)
{ struct vt1211_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sensor_attr_2 =
to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int fn = sensor_attr_2->nr; int reg; unsignedlong val; int err;
err = kstrtoul(buf, 10, &val); if (err) return err;
/* --------------------------------------------------------------------- * PWM auto point definitions * ix = [0-1] * ap = [0-3]
* --------------------------------------------------------------------- */
/* * pwm[ix+1]_auto_point[ap+1]_temp mapping table: * Note that there is only a single set of temp auto points that controls both * PWM controllers. We still create 2 sets of sysfs files to make it look * more consistent even though they map to the same registers. * * ix ap : description * ------------------- * 0 0 : pwm1/2 off temperature (pwm_auto_temp[0]) * 0 1 : pwm1/2 low speed temperature (pwm_auto_temp[1]) * 0 2 : pwm1/2 high speed temperature (pwm_auto_temp[2]) * 0 3 : pwm1/2 full speed temperature (pwm_auto_temp[3]) * 1 0 : pwm1/2 off temperature (pwm_auto_temp[0]) * 1 1 : pwm1/2 low speed temperature (pwm_auto_temp[1]) * 1 2 : pwm1/2 high speed temperature (pwm_auto_temp[2]) * 1 3 : pwm1/2 full speed temperature (pwm_auto_temp[3])
*/
static ssize_t show_pwm_auto_point_temp(struct device *dev, struct device_attribute *attr, char *buf)
{ struct vt1211_data *data = vt1211_update_device(dev); struct sensor_device_attribute_2 *sensor_attr_2 =
to_sensor_dev_attr_2(attr); int ix = sensor_attr_2->index; int ap = sensor_attr_2->nr;
/* * Initialize the interrupt mode (if request at module load time). * The VT1211 implements 3 different modes for clearing interrupts: * 0: Clear INT when status register is read. Regenerate INT as long * as temp stays above hysteresis limit. * 1: Clear INT when status register is read. DON'T regenerate INT * until temp falls below hysteresis limit and exceeds hot limit * again. * 2: Clear INT when temp falls below max limit. * * The driver only allows to force mode 0 since that's the only one * that makes sense for 'sensors'
*/ if (int_mode == 0) {
vt1211_write8(data, VT1211_REG_TEMP1_CONFIG, 0);
vt1211_write8(data, VT1211_REG_TEMP2_CONFIG, 0);
}
/* Fill in some hard wired values into our data struct */
data->pwm_auto_pwm[0][3] = 255;
data->pwm_auto_pwm[1][3] = 255;
}
for (i = 0; i < ARRAY_SIZE(vt1211_in_attr_group); i++)
sysfs_remove_group(&dev->kobj, &vt1211_in_attr_group[i]);
for (i = 0; i < ARRAY_SIZE(vt1211_temp_attr_group); i++)
sysfs_remove_group(&dev->kobj, &vt1211_temp_attr_group[i]);
for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) {
device_remove_file(dev,
&vt1211_sysfs_fan_pwm[i].dev_attr);
} for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++)
device_remove_file(dev, &vt1211_sysfs_misc[i]);
}
staticint vt1211_probe(struct platform_device *pdev)
{ struct device *dev = &pdev->dev; struct vt1211_data *data; struct resource *res; int i, err;
data = devm_kzalloc(dev, sizeof(struct vt1211_data), GFP_KERNEL); if (!data) return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(dev, res->start, resource_size(res),
DRVNAME)) {
dev_err(dev, "Failed to request region 0x%lx-0x%lx\n",
(unsignedlong)res->start, (unsignedlong)res->end); return -EBUSY;
}
data->addr = res->start;
data->name = DRVNAME;
mutex_init(&data->update_lock);
platform_set_drvdata(pdev, data);
/* Initialize the VT1211 chip */
vt1211_init_device(data);
/* Create sysfs interface files */ for (i = 0; i < ARRAY_SIZE(vt1211_in_attr_group); i++) { if (ISVOLT(i, data->uch_config)) {
err = sysfs_create_group(&dev->kobj,
&vt1211_in_attr_group[i]); if (err) goto EXIT_DEV_REMOVE;
}
} for (i = 0; i < ARRAY_SIZE(vt1211_temp_attr_group); i++) { if (ISTEMP(i, data->uch_config)) {
err = sysfs_create_group(&dev->kobj,
&vt1211_temp_attr_group[i]); if (err) goto EXIT_DEV_REMOVE;
}
} for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) {
err = device_create_file(dev,
&vt1211_sysfs_fan_pwm[i].dev_attr); if (err) goto EXIT_DEV_REMOVE;
} for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) {
err = device_create_file(dev,
&vt1211_sysfs_misc[i]); if (err) goto EXIT_DEV_REMOVE;
}
staticint __init vt1211_init(void)
{ int err; unsignedshort address = 0;
err = vt1211_find(SIO_REG_CIP1, &address); if (err) {
err = vt1211_find(SIO_REG_CIP2, &address); if (err) gotoEXIT;
}
if ((uch_config < -1) || (uch_config > 31)) {
err = -EINVAL;
pr_warn("Invalid UCH configuration %d. Choose a value between 0 and 31.\n",
uch_config); gotoEXIT;
}
if ((int_mode < -1) || (int_mode > 0)) {
err = -EINVAL;
pr_warn("Invalid interrupt mode %d. Only mode 0 is supported.\n",
int_mode); gotoEXIT;
}
err = platform_driver_register(&vt1211_driver); if (err) gotoEXIT;
/* Sets global pdev as a side effect */
err = vt1211_device_add(address); if (err) goto EXIT_DRV_UNREGISTER;
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.