📄 adm1031.c
字号:
value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr], FAN_DIV_FROM_REG(data->fan_div[nr])) : 0; return sprintf(buf, "%d\n", value);}static ssize_t show_fan_div(struct device *dev, char *buf, int nr){ struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr]));}static ssize_t show_fan_min(struct device *dev, char *buf, int nr){ struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], FAN_DIV_FROM_REG(data->fan_div[nr])));}static ssize_tset_fan_min(struct device *dev, const char *buf, size_t count, int nr){ struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int val; down(&data->update_lock); val = simple_strtol(buf, NULL, 10); if (val) { data->fan_min[nr] = FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr])); } else { data->fan_min[nr] = 0xff; } adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]); up(&data->update_lock); return count;}static ssize_tset_fan_div(struct device *dev, const char *buf, size_t count, int nr){ struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int val; u8 tmp; int old_div = FAN_DIV_FROM_REG(data->fan_div[nr]); int new_min; val = simple_strtol(buf, NULL, 10); tmp = val == 8 ? 0xc0 : val == 4 ? 0x80 : val == 2 ? 0x40 : val == 1 ? 0x00 : 0xff; if (tmp == 0xff) return -EINVAL; down(&data->update_lock); data->fan_div[nr] = (tmp & 0xC0) | (0x3f & data->fan_div[nr]); new_min = data->fan_min[nr] * old_div / FAN_DIV_FROM_REG(data->fan_div[nr]); data->fan_min[nr] = new_min > 0xff ? 0xff : new_min; data->fan[nr] = data->fan[nr] * old_div / FAN_DIV_FROM_REG(data->fan_div[nr]); adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr), data->fan_div[nr]); adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]); up(&data->update_lock); return count;}#define fan_offset(offset) \static ssize_t show_fan_##offset (struct device *dev, char *buf) \{ \ return show_fan(dev, buf, 0x##offset - 1); \} \static ssize_t show_fan_##offset##_min (struct device *dev, char *buf) \{ \ return show_fan_min(dev, buf, 0x##offset - 1); \} \static ssize_t show_fan_##offset##_div (struct device *dev, char *buf) \{ \ return show_fan_div(dev, buf, 0x##offset - 1); \} \static ssize_t set_fan_##offset##_min (struct device *dev, \ const char *buf, size_t count) \{ \ return set_fan_min(dev, buf, count, 0x##offset - 1); \} \static ssize_t set_fan_##offset##_div (struct device *dev, \ const char *buf, size_t count) \{ \ return set_fan_div(dev, buf, count, 0x##offset - 1); \} \static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \ NULL); \static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ show_fan_##offset##_min, set_fan_##offset##_min); \static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ show_fan_##offset##_div, set_fan_##offset##_div); \static DEVICE_ATTR(auto_fan##offset##_min_pwm, S_IRUGO | S_IWUSR, \ show_pwm_##offset, set_pwm_##offset)fan_offset(1);fan_offset(2);/* Temps */static ssize_t show_temp(struct device *dev, char *buf, int nr){ struct adm1031_data *data = adm1031_update_device(dev); int ext; ext = nr == 0 ? ((data->ext_temp[nr] >> 6) & 0x3) * 2 : (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7)); return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext));}static ssize_t show_temp_min(struct device *dev, char *buf, int nr){ struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr]));}static ssize_t show_temp_max(struct device *dev, char *buf, int nr){ struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr]));}static ssize_t show_temp_crit(struct device *dev, char *buf, int nr){ struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr]));}static ssize_tset_temp_min(struct device *dev, const char *buf, size_t count, int nr){ struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int val; val = simple_strtol(buf, NULL, 10); val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); down(&data->update_lock); data->temp_min[nr] = TEMP_TO_REG(val); adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr), data->temp_min[nr]); up(&data->update_lock); return count;}static ssize_tset_temp_max(struct device *dev, const char *buf, size_t count, int nr){ struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int val; val = simple_strtol(buf, NULL, 10); val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); down(&data->update_lock); data->temp_max[nr] = TEMP_TO_REG(val); adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr), data->temp_max[nr]); up(&data->update_lock); return count;}static ssize_tset_temp_crit(struct device *dev, const char *buf, size_t count, int nr){ struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int val; val = simple_strtol(buf, NULL, 10); val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); down(&data->update_lock); data->temp_crit[nr] = TEMP_TO_REG(val); adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr), data->temp_crit[nr]); up(&data->update_lock); return count;}#define temp_reg(offset) \static ssize_t show_temp_##offset (struct device *dev, char *buf) \{ \ return show_temp(dev, buf, 0x##offset - 1); \} \static ssize_t show_temp_##offset##_min (struct device *dev, char *buf) \{ \ return show_temp_min(dev, buf, 0x##offset - 1); \} \static ssize_t show_temp_##offset##_max (struct device *dev, char *buf) \{ \ return show_temp_max(dev, buf, 0x##offset - 1); \} \static ssize_t show_temp_##offset##_crit (struct device *dev, char *buf) \{ \ return show_temp_crit(dev, buf, 0x##offset - 1); \} \static ssize_t set_temp_##offset##_min (struct device *dev, \ const char *buf, size_t count) \{ \ return set_temp_min(dev, buf, count, 0x##offset - 1); \} \static ssize_t set_temp_##offset##_max (struct device *dev, \ const char *buf, size_t count) \{ \ return set_temp_max(dev, buf, count, 0x##offset - 1); \} \static ssize_t set_temp_##offset##_crit (struct device *dev, \ const char *buf, size_t count) \{ \ return set_temp_crit(dev, buf, count, 0x##offset - 1); \} \static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \ NULL); \static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ show_temp_##offset##_min, set_temp_##offset##_min); \static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ show_temp_##offset##_max, set_temp_##offset##_max); \static DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR, \ show_temp_##offset##_crit, set_temp_##offset##_crit)temp_reg(1);temp_reg(2);temp_reg(3);/* Alarms */static ssize_t show_alarms(struct device *dev, char *buf){ struct adm1031_data *data = adm1031_update_device(dev); return sprintf(buf, "%d\n", data->alarm);}static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);static int adm1031_attach_adapter(struct i2c_adapter *adapter){ if (!(adapter->class & I2C_CLASS_HWMON)) return 0; return i2c_detect(adapter, &addr_data, adm1031_detect);}/* This function is called by i2c_detect */static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind){ struct i2c_client *new_client; struct adm1031_data *data; int err = 0; const char *name = ""; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) goto exit; if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) { err = -ENOMEM; goto exit; } memset(data, 0, sizeof(struct adm1031_data)); new_client = &data->client; i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &adm1031_driver; new_client->flags = 0; if (kind < 0) { int id, co; id = i2c_smbus_read_byte_data(new_client, 0x3d); co = i2c_smbus_read_byte_data(new_client, 0x3e); if (!((id == 0x31 || id == 0x30) && co == 0x41)) goto exit_free; kind = (id == 0x30) ? adm1030 : adm1031; } if (kind <= 0) kind = adm1031; /* Given the detected chip type, set the chip name and the * auto fan control helper table. */ if (kind == adm1030) { name = "adm1030"; data->chan_select_table = &auto_channel_select_table_adm1030; } else if (kind == adm1031) { name = "adm1031"; data->chan_select_table = &auto_channel_select_table_adm1031; } data->chip_type = kind; strlcpy(new_client->name, name, I2C_NAME_SIZE); new_client->id = adm1031_id++; data->valid = 0; init_MUTEX(&data->update_lock); /* Tell the I2C layer a new client has arrived */ if ((err = i2c_attach_client(new_client))) goto exit_free; /* Initialize the ADM1031 chip */ adm1031_init_client(new_client); /* Register sysfs hooks */ device_create_file(&new_client->dev, &dev_attr_fan1_input); device_create_file(&new_client->dev, &dev_attr_fan1_div); device_create_file(&new_client->dev, &dev_attr_fan1_min); device_create_file(&new_client->dev, &dev_attr_fan1_pwm); device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel); device_create_file(&new_client->dev, &dev_attr_temp1_input); device_create_file(&new_client->dev, &dev_attr_temp1_min); device_create_file(&new_client->dev, &dev_attr_temp1_max); device_create_file(&new_client->dev, &dev_attr_temp1_crit); device_create_file(&new_client->dev, &dev_attr_temp2_input); device_create_file(&new_client->dev, &dev_attr_temp2_min); device_create_file(&new_client->dev, &dev_attr_temp2_max); device_create_file(&new_client->dev, &dev_attr_temp2_crit); device_create_file(&new_client->dev, &dev_attr_auto_temp1_off); device_create_file(&new_client->dev, &dev_attr_auto_temp1_min); device_create_file(&new_client->dev, &dev_attr_auto_temp1_max); device_create_file(&new_client->dev, &dev_attr_auto_temp2_off); device_create_file(&new_client->dev, &dev_attr_auto_temp2_min); device_create_file(&new_client->dev, &dev_attr_auto_temp2_max); device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm); device_create_file(&new_client->dev, &dev_attr_alarms); if (kind == adm1031) { device_create_file(&new_client->dev, &dev_attr_fan2_input); device_create_file(&new_client->dev, &dev_attr_fan2_div); device_create_file(&new_client->dev, &dev_attr_fan2_min); device_create_file(&new_client->dev, &dev_attr_fan2_pwm); device_create_file(&new_client->dev, &dev_attr_auto_fan2_channel); device_create_file(&new_client->dev, &dev_attr_temp3_input); device_create_file(&new_client->dev, &dev_attr_temp3_min); device_create_file(&new_client->dev, &dev_attr_temp3_max); device_create_file(&new_client->dev, &dev_attr_temp3_crit); device_create_file(&new_client->dev, &dev_attr_auto_temp3_off); device_create_file(&new_client->dev, &dev_attr_auto_temp3_min); device_create_file(&new_client->dev, &dev_attr_auto_temp3_max); device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm); } return 0;exit_free: kfree(new_client);exit: return err;}static int adm1031_detach_client(struct i2c_client *client){ int ret; if ((ret = i2c_detach_client(client)) != 0) { return ret; } kfree(client); return 0;}static void adm1031_init_client(struct i2c_client *client){ unsigned int read_val; unsigned int mask; struct adm1031_data *data = i2c_get_clientdata(client); mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE); if (data->chip_type == adm1031) { mask |= (ADM1031_CONF2_PWM2_ENABLE | ADM1031_CONF2_TACH2_ENABLE); } /* Initialize the ADM1031 chip (enables fan speed reading ) */ read_val = adm1031_read_value(client, ADM1031_REG_CONF2); if ((read_val | mask) != read_val) { adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask); } read_val = adm1031_read_value(client, ADM1031_REG_CONF1); if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) { adm1031_write_value(client, ADM1031_REG_CONF1, read_val | ADM1031_CONF1_MONITOR_ENABLE); }}static struct adm1031_data *adm1031_update_device(struct device *dev){ struct i2c_client *client = to_i2c_client(dev); struct adm1031_data *data = i2c_get_clientdata(client); int chan; down(&data->update_lock); if ((jiffies - data->last_updated > HZ + HZ / 2) || (jiffies < data->last_updated) || !data->valid) { dev_dbg(&client->dev, "Starting adm1031 update\n"); for (chan = 0; chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) { u8 oldh, newh; oldh = adm1031_read_value(client, ADM1031_REG_TEMP(chan)); data->ext_temp[chan] = adm1031_read_value(client, ADM1031_REG_EXT_TEMP); newh = adm1031_read_value(client, ADM1031_REG_TEMP(chan)); if (newh != oldh) { data->ext_temp[chan] = adm1031_read_value(client, ADM1031_REG_EXT_TEMP);#ifdef DEBUG oldh = adm1031_read_value(client, ADM1031_REG_TEMP(chan)); /* oldh is actually newer */ if (newh != oldh) dev_warn(&client->dev, "Remote temperature may be " "wrong.\n");#endif } data->temp[chan] = newh; data->temp_min[chan] = adm1031_read_value(client, ADM1031_REG_TEMP_MIN(chan)); data->temp_max[chan] = adm1031_read_value(client, ADM1031_REG_TEMP_MAX(chan)); data->temp_crit[chan] = adm1031_read_value(client, ADM1031_REG_TEMP_CRIT(chan)); data->auto_temp[chan] = adm1031_read_value(client, ADM1031_REG_AUTO_TEMP(chan)); } data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1); data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2); data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0)) | (adm1031_read_value(client, ADM1031_REG_STATUS(1)) << 8); if (data->chip_type == adm1030) { data->alarm &= 0xc0ff; } for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) { data->fan_div[chan] = adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan)); data->fan_min[chan] = adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan)); data->fan[chan] = adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan)); data->pwm[chan] = 0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >> (4*chan)); } data->last_updated = jiffies; data->valid = 1; } up(&data->update_lock); return data;}static int __init sensors_adm1031_init(void){ return i2c_add_driver(&adm1031_driver);}static void __exit sensors_adm1031_exit(void){ i2c_del_driver(&adm1031_driver);}MODULE_AUTHOR("Alexandre d'Alton <alex@alexdalton.org>");MODULE_DESCRIPTION("ADM1031/ADM1030 driver");MODULE_LICENSE("GPL");module_init(sensors_adm1031_init);module_exit(sensors_adm1031_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -