📄 lm78.c
字号:
static ssize_t set_fan_1_div(struct device *dev, const char *buf, size_t count){ return set_fan_div(dev, buf, count, 0) ;}static ssize_t set_fan_2_div(struct device *dev, const char *buf, size_t count){ return set_fan_div(dev, buf, count, 1) ;}show_fan_offset(1);show_fan_offset(2);show_fan_offset(3);/* Fan 3 divisor is locked in H/W */static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_1_div, set_fan_1_div);static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, show_fan_2_div, set_fan_2_div);static DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_3_div, NULL);/* VID */static ssize_t show_vid(struct device *dev, char *buf){ struct lm78_data *data = lm78_update_device(dev); return sprintf(buf, "%d\n", VID_FROM_REG(data->vid));}static DEVICE_ATTR(in0_ref, S_IRUGO, show_vid, NULL);/* Alarms */static ssize_t show_alarms(struct device *dev, char *buf){ struct lm78_data *data = lm78_update_device(dev); return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->alarms));}static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);/* This function is called when: * lm78_driver is inserted (when this module is loaded), for each available adapter * when a new adapter is inserted (and lm78_driver is still present) */static int lm78_attach_adapter(struct i2c_adapter *adapter){ if (!(adapter->class & I2C_CLASS_HWMON)) return 0; return i2c_detect(adapter, &addr_data, lm78_detect);}/* This function is called by i2c_detect */int lm78_detect(struct i2c_adapter *adapter, int address, int kind){ int i, err; struct i2c_client *new_client; struct lm78_data *data; const char *client_name = ""; int is_isa = i2c_is_isa_adapter(adapter); if (!is_isa && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { err = -ENODEV; goto ERROR0; } /* Reserve the ISA region */ if (is_isa) if (!request_region(address, LM78_EXTENT, "lm78")) { err = -EBUSY; goto ERROR0; } /* Probe whether there is anything available on this address. Already done for SMBus clients */ if (kind < 0) { if (is_isa) {#define REALLY_SLOW_IO /* We need the timeouts for at least some LM78-like chips. But only if we read 'undefined' registers. */ i = inb_p(address + 1); if (inb_p(address + 2) != i) { err = -ENODEV; goto ERROR1; } if (inb_p(address + 3) != i) { err = -ENODEV; goto ERROR1; } if (inb_p(address + 7) != i) { err = -ENODEV; goto ERROR1; }#undef REALLY_SLOW_IO /* Let's just hope nothing breaks here */ i = inb_p(address + 5) & 0x7f; outb_p(~i & 0x7f, address + 5); if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { outb_p(i, address + 5); err = -ENODEV; goto ERROR1; } } } /* OK. For now, we presume we have a valid client. We now create the client structure, even though we cannot fill it completely yet. But it allows us to access lm78_{read,write}_value. */ if (!(data = kmalloc(sizeof(struct lm78_data), GFP_KERNEL))) { err = -ENOMEM; goto ERROR1; } memset(data, 0, sizeof(struct lm78_data)); new_client = &data->client; if (is_isa) init_MUTEX(&data->lock); i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &lm78_driver; new_client->flags = 0; /* Now, we do the remaining detection. */ if (kind < 0) { if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) { err = -ENODEV; goto ERROR2; } if (!is_isa && (lm78_read_value( new_client, LM78_REG_I2C_ADDR) != address)) { err = -ENODEV; goto ERROR2; } } /* Determine the chip type. */ if (kind <= 0) { i = lm78_read_value(new_client, LM78_REG_CHIPID); if (i == 0x00 || i == 0x20) kind = lm78; else if (i == 0x40) kind = lm78j; else if ((i & 0xfe) == 0xc0) kind = lm79; else { if (kind == 0) dev_warn(&adapter->dev, "Ignoring 'force' " "parameter for unknown chip at " "adapter %d, address 0x%02x\n", i2c_adapter_id(adapter), address); err = -ENODEV; goto ERROR2; } } if (kind == lm78) { client_name = "lm78"; } else if (kind == lm78j) { client_name = "lm78-j"; } else if (kind == lm79) { client_name = "lm79"; } /* Fill in the remaining client fields and put into the global list */ strlcpy(new_client->name, client_name, I2C_NAME_SIZE); data->type = kind; 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 ERROR2; /* Initialize the LM78 chip */ lm78_init_client(new_client); /* A few vars need to be filled upon startup */ for (i = 0; i < 3; i++) { data->fan_min[i] = lm78_read_value(new_client, LM78_REG_FAN_MIN(i)); } /* Register sysfs hooks */ device_create_file(&new_client->dev, &dev_attr_in0_input); device_create_file(&new_client->dev, &dev_attr_in0_min); device_create_file(&new_client->dev, &dev_attr_in0_max); device_create_file(&new_client->dev, &dev_attr_in1_input); device_create_file(&new_client->dev, &dev_attr_in1_min); device_create_file(&new_client->dev, &dev_attr_in1_max); device_create_file(&new_client->dev, &dev_attr_in2_input); device_create_file(&new_client->dev, &dev_attr_in2_min); device_create_file(&new_client->dev, &dev_attr_in2_max); device_create_file(&new_client->dev, &dev_attr_in3_input); device_create_file(&new_client->dev, &dev_attr_in3_min); device_create_file(&new_client->dev, &dev_attr_in3_max); device_create_file(&new_client->dev, &dev_attr_in4_input); device_create_file(&new_client->dev, &dev_attr_in4_min); device_create_file(&new_client->dev, &dev_attr_in4_max); device_create_file(&new_client->dev, &dev_attr_in5_input); device_create_file(&new_client->dev, &dev_attr_in5_min); device_create_file(&new_client->dev, &dev_attr_in5_max); device_create_file(&new_client->dev, &dev_attr_in6_input); device_create_file(&new_client->dev, &dev_attr_in6_min); device_create_file(&new_client->dev, &dev_attr_in6_max); device_create_file(&new_client->dev, &dev_attr_temp1_input); device_create_file(&new_client->dev, &dev_attr_temp1_max); device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); device_create_file(&new_client->dev, &dev_attr_fan1_input); device_create_file(&new_client->dev, &dev_attr_fan1_min); device_create_file(&new_client->dev, &dev_attr_fan1_div); device_create_file(&new_client->dev, &dev_attr_fan2_input); device_create_file(&new_client->dev, &dev_attr_fan2_min); device_create_file(&new_client->dev, &dev_attr_fan2_div); device_create_file(&new_client->dev, &dev_attr_fan3_input); device_create_file(&new_client->dev, &dev_attr_fan3_min); device_create_file(&new_client->dev, &dev_attr_fan3_div); device_create_file(&new_client->dev, &dev_attr_alarms); device_create_file(&new_client->dev, &dev_attr_in0_ref); return 0;ERROR2: kfree(data);ERROR1: if (is_isa) release_region(address, LM78_EXTENT);ERROR0: return err;}static int lm78_detach_client(struct i2c_client *client){ int err; /* release ISA region first */ if(i2c_is_isa_client(client)) release_region(client->addr, LM78_EXTENT); /* now it's safe to scrap the rest */ if ((err = i2c_detach_client(client))) { dev_err(&client->dev, "Client deregistration failed, client not detached.\n"); return err; } kfree(i2c_get_clientdata(client)); return 0;}/* The SMBus locks itself, but ISA access must be locked explicitely! We don't want to lock the whole ISA bus, so we lock each client separately. We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, would slow down the LM78 access and should not be necessary. There are some ugly typecasts here, but the good new is - they should nowhere else be necessary! */static int lm78_read_value(struct i2c_client *client, u8 reg){ int res; if (i2c_is_isa_client(client)) { struct lm78_data *data = i2c_get_clientdata(client); down(&data->lock); outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); res = inb_p(client->addr + LM78_DATA_REG_OFFSET); up(&data->lock); return res; } else return i2c_smbus_read_byte_data(client, reg);}/* The SMBus locks itself, but ISA access muse be locked explicitely! We don't want to lock the whole ISA bus, so we lock each client separately. We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, would slow down the LM78 access and should not be necessary. There are some ugly typecasts here, but the good new is - they should nowhere else be necessary! */static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value){ if (i2c_is_isa_client(client)) { struct lm78_data *data = i2c_get_clientdata(client); down(&data->lock); outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); outb_p(value, client->addr + LM78_DATA_REG_OFFSET); up(&data->lock); return 0; } else return i2c_smbus_write_byte_data(client, reg, value);}/* Called when we have found a new LM78. It should set limits, etc. */static void lm78_init_client(struct i2c_client *client){ struct lm78_data *data = i2c_get_clientdata(client); int vid; /* Reset all except Watchdog values and last conversion values This sets fan-divs to 2, among others */ lm78_write_value(client, LM78_REG_CONFIG, 0x80); vid = lm78_read_value(client, LM78_REG_VID_FANDIV) & 0x0f; if (data->type == lm79) vid |= (lm78_read_value(client, LM78_REG_CHIPID) & 0x01) << 4; else vid |= 0x10; vid = VID_FROM_REG(vid); /* Start monitoring */ lm78_write_value(client, LM78_REG_CONFIG, (lm78_read_value(client, LM78_REG_CONFIG) & 0xf7) | 0x01);}static struct lm78_data *lm78_update_device(struct device *dev){ struct i2c_client *client = to_i2c_client(dev); struct lm78_data *data = i2c_get_clientdata(client); int i; down(&data->update_lock); if ((jiffies - data->last_updated > HZ + HZ / 2) || (jiffies < data->last_updated) || !data->valid) { dev_dbg(&client->dev, "Starting lm78 update\n"); for (i = 0; i <= 6; i++) { data->in[i] = lm78_read_value(client, LM78_REG_IN(i)); data->in_min[i] = lm78_read_value(client, LM78_REG_IN_MIN(i)); data->in_max[i] = lm78_read_value(client, LM78_REG_IN_MAX(i)); } for (i = 0; i < 3; i++) { data->fan[i] = lm78_read_value(client, LM78_REG_FAN(i)); data->fan_min[i] = lm78_read_value(client, LM78_REG_FAN_MIN(i)); } data->temp = lm78_read_value(client, LM78_REG_TEMP); data->temp_over = lm78_read_value(client, LM78_REG_TEMP_OVER); data->temp_hyst = lm78_read_value(client, LM78_REG_TEMP_HYST); i = lm78_read_value(client, LM78_REG_VID_FANDIV); data->vid = i & 0x0f; if (data->type == lm79) data->vid |= (lm78_read_value(client, LM78_REG_CHIPID) & 0x01) << 4; else data->vid |= 0x10; data->fan_div[0] = (i >> 4) & 0x03; data->fan_div[1] = i >> 6; data->alarms = lm78_read_value(client, LM78_REG_ALARM1) + (lm78_read_value(client, LM78_REG_ALARM2) << 8); data->last_updated = jiffies; data->valid = 1; data->fan_div[2] = 1; } up(&data->update_lock); return data;}static int __init sm_lm78_init(void){ return i2c_add_driver(&lm78_driver);}static void __exit sm_lm78_exit(void){ i2c_del_driver(&lm78_driver);}MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver");MODULE_LICENSE("GPL");module_init(sm_lm78_init);module_exit(sm_lm78_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -