vt8231.c

来自「linux 内核源代码」· C语言 代码 · 共 972 行 · 第 1/2 页

C
972
字号
}static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,		const char *buf, size_t count){	struct vt8231_data *data = dev_get_drvdata(dev);	struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);	unsigned long val = simple_strtoul(buf, NULL, 10);	int nr = sensor_attr->index;	int old = vt8231_read_value(data, VT8231_REG_FANDIV);	long min = FAN_FROM_REG(data->fan_min[nr],				 DIV_FROM_REG(data->fan_div[nr]));	mutex_lock(&data->update_lock);	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;	}	/* Correct the fan minimum speed */	data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));	vt8231_write_value(data, VT8231_REG_FAN_MIN(nr), data->fan_min[nr]);	old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4);	vt8231_write_value(data, VT8231_REG_FANDIV, old);	mutex_unlock(&data->update_lock);	return count;}#define define_fan_sysfs(offset)					\static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO,			\		show_fan, NULL, offset - 1);				\static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR,		\		show_fan_div, set_fan_div, offset - 1);			\static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR,		\		show_fan_min, set_fan_min, offset - 1)define_fan_sysfs(1);define_fan_sysfs(2);/* Alarms */static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,			   char *buf){	struct vt8231_data *data = vt8231_update_device(dev);	return sprintf(buf, "%d\n", data->alarms);}static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);static ssize_t show_name(struct device *dev, struct device_attribute			 *devattr, char *buf){	struct vt8231_data *data = dev_get_drvdata(dev);	return sprintf(buf, "%s\n", data->name);}static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);static struct attribute *vt8231_attributes_temps[6][4] = {	{		&dev_attr_temp1_input.attr,		&dev_attr_temp1_max_hyst.attr,		&dev_attr_temp1_max.attr,		NULL	}, {		&sensor_dev_attr_temp2_input.dev_attr.attr,		&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,		&sensor_dev_attr_temp2_max.dev_attr.attr,		NULL	}, {		&sensor_dev_attr_temp3_input.dev_attr.attr,		&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,		&sensor_dev_attr_temp3_max.dev_attr.attr,		NULL	}, {		&sensor_dev_attr_temp4_input.dev_attr.attr,		&sensor_dev_attr_temp4_max_hyst.dev_attr.attr,		&sensor_dev_attr_temp4_max.dev_attr.attr,		NULL	}, {		&sensor_dev_attr_temp5_input.dev_attr.attr,		&sensor_dev_attr_temp5_max_hyst.dev_attr.attr,		&sensor_dev_attr_temp5_max.dev_attr.attr,		NULL	}, {		&sensor_dev_attr_temp6_input.dev_attr.attr,		&sensor_dev_attr_temp6_max_hyst.dev_attr.attr,		&sensor_dev_attr_temp6_max.dev_attr.attr,		NULL	}};static const struct attribute_group vt8231_group_temps[6] = {	{ .attrs = vt8231_attributes_temps[0] },	{ .attrs = vt8231_attributes_temps[1] },	{ .attrs = vt8231_attributes_temps[2] },	{ .attrs = vt8231_attributes_temps[3] },	{ .attrs = vt8231_attributes_temps[4] },	{ .attrs = vt8231_attributes_temps[5] },};static struct attribute *vt8231_attributes_volts[6][4] = {	{		&sensor_dev_attr_in0_input.dev_attr.attr,		&sensor_dev_attr_in0_min.dev_attr.attr,		&sensor_dev_attr_in0_max.dev_attr.attr,		NULL	}, {		&sensor_dev_attr_in1_input.dev_attr.attr,		&sensor_dev_attr_in1_min.dev_attr.attr,		&sensor_dev_attr_in1_max.dev_attr.attr,		NULL	}, {		&sensor_dev_attr_in2_input.dev_attr.attr,		&sensor_dev_attr_in2_min.dev_attr.attr,		&sensor_dev_attr_in2_max.dev_attr.attr,		NULL	}, {		&sensor_dev_attr_in3_input.dev_attr.attr,		&sensor_dev_attr_in3_min.dev_attr.attr,		&sensor_dev_attr_in3_max.dev_attr.attr,		NULL	}, {		&sensor_dev_attr_in4_input.dev_attr.attr,		&sensor_dev_attr_in4_min.dev_attr.attr,		&sensor_dev_attr_in4_max.dev_attr.attr,		NULL	}, {		&dev_attr_in5_input.attr,		&dev_attr_in5_min.attr,		&dev_attr_in5_max.attr,		NULL	}};static const struct attribute_group vt8231_group_volts[6] = {	{ .attrs = vt8231_attributes_volts[0] },	{ .attrs = vt8231_attributes_volts[1] },	{ .attrs = vt8231_attributes_volts[2] },	{ .attrs = vt8231_attributes_volts[3] },	{ .attrs = vt8231_attributes_volts[4] },	{ .attrs = vt8231_attributes_volts[5] },};static struct attribute *vt8231_attributes[] = {	&sensor_dev_attr_fan1_input.dev_attr.attr,	&sensor_dev_attr_fan2_input.dev_attr.attr,	&sensor_dev_attr_fan1_min.dev_attr.attr,	&sensor_dev_attr_fan2_min.dev_attr.attr,	&sensor_dev_attr_fan1_div.dev_attr.attr,	&sensor_dev_attr_fan2_div.dev_attr.attr,	&dev_attr_alarms.attr,	&dev_attr_name.attr,	NULL};static const struct attribute_group vt8231_group = {	.attrs = vt8231_attributes,};static struct platform_driver vt8231_driver = {	.driver = {		.owner	= THIS_MODULE,		.name	= "vt8231",	},	.probe	= vt8231_probe,	.remove	= __devexit_p(vt8231_remove),};static struct pci_device_id vt8231_pci_ids[] = {	{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4) },	{ 0, }};MODULE_DEVICE_TABLE(pci, vt8231_pci_ids);static int __devinit vt8231_pci_probe(struct pci_dev *dev,			 	      const struct pci_device_id *id);static struct pci_driver vt8231_pci_driver = {	.name		= "vt8231",	.id_table	= vt8231_pci_ids,	.probe		= vt8231_pci_probe,};static int vt8231_probe(struct platform_device *pdev){	struct resource *res;	struct vt8231_data *data;	int err = 0, i;	/* Reserve the ISA region */	res = platform_get_resource(pdev, IORESOURCE_IO, 0);	if (!request_region(res->start, VT8231_EXTENT,			    vt8231_driver.driver.name)) {		dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n",			(unsigned long)res->start, (unsigned long)res->end);		return -ENODEV;	}	if (!(data = kzalloc(sizeof(struct vt8231_data), GFP_KERNEL))) {		err = -ENOMEM;		goto exit_release;	}	platform_set_drvdata(pdev, data);	data->addr = res->start;	data->name = "vt8231";	mutex_init(&data->update_lock);	vt8231_init_device(data);	/* Register sysfs hooks */	if ((err = sysfs_create_group(&pdev->dev.kobj, &vt8231_group)))		goto exit_free;	/* Must update device information to find out the config field */	data->uch_config = vt8231_read_value(data, VT8231_REG_UCH_CONFIG);	for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) {		if (ISTEMP(i, data->uch_config)) {			if ((err = sysfs_create_group(&pdev->dev.kobj,					&vt8231_group_temps[i])))				goto exit_remove_files;		}	}	for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) {		if (ISVOLT(i, data->uch_config)) {			if ((err = sysfs_create_group(&pdev->dev.kobj,					&vt8231_group_volts[i])))				goto exit_remove_files;		}	}	data->hwmon_dev = hwmon_device_register(&pdev->dev);	if (IS_ERR(data->hwmon_dev)) {		err = PTR_ERR(data->hwmon_dev);		goto exit_remove_files;	}	return 0;exit_remove_files:	for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)		sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_volts[i]);	for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)		sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]);	sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);exit_free:	platform_set_drvdata(pdev, NULL);	kfree(data);exit_release:	release_region(res->start, VT8231_EXTENT);	return err;}static int __devexit vt8231_remove(struct platform_device *pdev){	struct vt8231_data *data = platform_get_drvdata(pdev);	int i;	hwmon_device_unregister(data->hwmon_dev);	for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)		sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_volts[i]);	for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)		sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]);	sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);	release_region(data->addr, VT8231_EXTENT);	platform_set_drvdata(pdev, NULL);	kfree(data);	return 0;}static void vt8231_init_device(struct vt8231_data *data){	vt8231_write_value(data, VT8231_REG_TEMP1_CONFIG, 0);	vt8231_write_value(data, VT8231_REG_TEMP2_CONFIG, 0);}static struct vt8231_data *vt8231_update_device(struct device *dev){	struct vt8231_data *data = dev_get_drvdata(dev);	int i;	u16 low;	mutex_lock(&data->update_lock);	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)	    || !data->valid) {		for (i = 0; i < 6; i++) {			if (ISVOLT(i, data->uch_config)) {				data->in[i] = vt8231_read_value(data,						regvolt[i]);				data->in_min[i] = vt8231_read_value(data,						regvoltmin[i]);				data->in_max[i] = vt8231_read_value(data,						regvoltmax[i]);			}		}		for (i = 0; i < 2; i++) {			data->fan[i] = vt8231_read_value(data,						VT8231_REG_FAN(i));			data->fan_min[i] = vt8231_read_value(data,						VT8231_REG_FAN_MIN(i));		}		low = vt8231_read_value(data, VT8231_REG_TEMP_LOW01);		low = (low >> 6) | ((low & 0x30) >> 2)		    | (vt8231_read_value(data, VT8231_REG_TEMP_LOW25) << 4);		for (i = 0; i < 6; i++) {			if (ISTEMP(i, data->uch_config)) {				data->temp[i] = (vt8231_read_value(data,						       regtemp[i]) << 2)						| ((low >> (2 * i)) & 0x03);				data->temp_max[i] = vt8231_read_value(data,						      regtempmax[i]);				data->temp_min[i] = vt8231_read_value(data,						      regtempmin[i]);			}		}		i = vt8231_read_value(data, VT8231_REG_FANDIV);		data->fan_div[0] = (i >> 4) & 0x03;		data->fan_div[1] = i >> 6;		data->alarms = vt8231_read_value(data, VT8231_REG_ALARM1) |			(vt8231_read_value(data, VT8231_REG_ALARM2) << 8);		/* Set alarm flags correctly */		if (!data->fan[0] && data->fan_min[0]) {			data->alarms |= 0x40;		} else if (data->fan[0] && !data->fan_min[0]) {			data->alarms &= ~0x40;		}		if (!data->fan[1] && data->fan_min[1]) {			data->alarms |= 0x80;		} else if (data->fan[1] && !data->fan_min[1]) {			data->alarms &= ~0x80;		}		data->last_updated = jiffies;		data->valid = 1;	}	mutex_unlock(&data->update_lock);	return data;}static int __devinit vt8231_device_add(unsigned short address){	struct resource res = {		.start	= address,		.end	= address + VT8231_EXTENT - 1,		.name	= "vt8231",		.flags	= IORESOURCE_IO,	};	int err;	pdev = platform_device_alloc("vt8231", address);	if (!pdev) {		err = -ENOMEM;		printk(KERN_ERR "vt8231: Device allocation failed\n");		goto exit;	}	err = platform_device_add_resources(pdev, &res, 1);	if (err) {		printk(KERN_ERR "vt8231: Device resource addition failed "		       "(%d)\n", err);		goto exit_device_put;	}	err = platform_device_add(pdev);	if (err) {		printk(KERN_ERR "vt8231: Device addition failed (%d)\n",		       err);		goto exit_device_put;	}	return 0;exit_device_put:	platform_device_put(pdev);exit:	return err;}static int __devinit vt8231_pci_probe(struct pci_dev *dev,				const struct pci_device_id *id){	u16 address, val;	if (force_addr) {		address = force_addr & 0xff00;		dev_warn(&dev->dev, "Forcing ISA address 0x%x\n",			 address);		if (PCIBIOS_SUCCESSFUL !=		    pci_write_config_word(dev, VT8231_BASE_REG, address | 1))			return -ENODEV;	}	if (PCIBIOS_SUCCESSFUL != pci_read_config_word(dev, VT8231_BASE_REG,							&val))		return -ENODEV;	address = val & ~(VT8231_EXTENT - 1);	if (address == 0) {		dev_err(&dev->dev, "base address not set -\				 upgrade BIOS or use force_addr=0xaddr\n");		return -ENODEV;	}	if (PCIBIOS_SUCCESSFUL != pci_read_config_word(dev, VT8231_ENABLE_REG,							&val))		return -ENODEV;	if (!(val & 0x0001)) {		dev_warn(&dev->dev, "enabling sensors\n");		if (PCIBIOS_SUCCESSFUL !=			pci_write_config_word(dev, VT8231_ENABLE_REG,							val | 0x0001))			return -ENODEV;	}	if (platform_driver_register(&vt8231_driver))		goto exit;	/* Sets global pdev as a side effect */	if (vt8231_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.	 */	/* We do, however, mark ourselves as using the PCI device to stop it	   getting unloaded. */	s_bridge = pci_dev_get(dev);	return -ENODEV;exit_unregister:	platform_driver_unregister(&vt8231_driver);exit:	return -ENODEV;}static int __init sm_vt8231_init(void){	return pci_register_driver(&vt8231_pci_driver);}static void __exit sm_vt8231_exit(void){	pci_unregister_driver(&vt8231_pci_driver);	if (s_bridge != NULL) {		platform_device_unregister(pdev);		platform_driver_unregister(&vt8231_driver);		pci_dev_put(s_bridge);		s_bridge = NULL;	}}MODULE_AUTHOR("Roger Lucas <roger@planbit.co.uk>");MODULE_DESCRIPTION("VT8231 sensors");MODULE_LICENSE("GPL");module_init(sm_vt8231_init);module_exit(sm_vt8231_exit);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?