vt1211.c
来自「linux 内核源代码」· C语言 代码 · 共 1,371 行 · 第 1/3 页
C
1,371 行
SENSOR_ATTR_2(temp##ix##_input, S_IRUGO, \ show_temp, NULL, SHOW_TEMP_INPUT, ix-1)static struct sensor_device_attribute_2 vt1211_sysfs_temp_input[] = { SENSOR_ATTR_TEMP_INPUT(1), SENSOR_ATTR_TEMP_INPUT(2), SENSOR_ATTR_TEMP_INPUT(3), SENSOR_ATTR_TEMP_INPUT(4), SENSOR_ATTR_TEMP_INPUT(5), SENSOR_ATTR_TEMP_INPUT(6), SENSOR_ATTR_TEMP_INPUT(7),};#define SENSOR_ATTR_TEMP_MAX(ix) \ SENSOR_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \ show_temp, set_temp, SHOW_SET_TEMP_MAX, ix-1)static struct sensor_device_attribute_2 vt1211_sysfs_temp_max[] = { SENSOR_ATTR_TEMP_MAX(1), SENSOR_ATTR_TEMP_MAX(2), SENSOR_ATTR_TEMP_MAX(3), SENSOR_ATTR_TEMP_MAX(4), SENSOR_ATTR_TEMP_MAX(5), SENSOR_ATTR_TEMP_MAX(6), SENSOR_ATTR_TEMP_MAX(7),};#define SENSOR_ATTR_TEMP_MAX_HYST(ix) \ SENSOR_ATTR_2(temp##ix##_max_hyst, S_IRUGO | S_IWUSR, \ show_temp, set_temp, SHOW_SET_TEMP_MAX_HYST, ix-1)static struct sensor_device_attribute_2 vt1211_sysfs_temp_max_hyst[] = { SENSOR_ATTR_TEMP_MAX_HYST(1), SENSOR_ATTR_TEMP_MAX_HYST(2), SENSOR_ATTR_TEMP_MAX_HYST(3), SENSOR_ATTR_TEMP_MAX_HYST(4), SENSOR_ATTR_TEMP_MAX_HYST(5), SENSOR_ATTR_TEMP_MAX_HYST(6), SENSOR_ATTR_TEMP_MAX_HYST(7),};#define SENSOR_ATTR_TEMP_ALARM(ix) \ SENSOR_ATTR_2(temp##ix##_alarm, S_IRUGO, \ show_temp, NULL, SHOW_TEMP_ALARM, ix-1)static struct sensor_device_attribute_2 vt1211_sysfs_temp_alarm[] = { SENSOR_ATTR_TEMP_ALARM(1), SENSOR_ATTR_TEMP_ALARM(2), SENSOR_ATTR_TEMP_ALARM(3), SENSOR_ATTR_TEMP_ALARM(4), SENSOR_ATTR_TEMP_ALARM(5), SENSOR_ATTR_TEMP_ALARM(6), SENSOR_ATTR_TEMP_ALARM(7),};#define SENSOR_ATTR_FAN(ix) \ SENSOR_ATTR_2(fan##ix##_input, S_IRUGO, \ show_fan, NULL, SHOW_FAN_INPUT, ix-1), \ SENSOR_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \ show_fan, set_fan, SHOW_SET_FAN_MIN, ix-1), \ SENSOR_ATTR_2(fan##ix##_div, S_IRUGO | S_IWUSR, \ show_fan, set_fan, SHOW_SET_FAN_DIV, ix-1), \ SENSOR_ATTR_2(fan##ix##_alarm, S_IRUGO, \ show_fan, NULL, SHOW_FAN_ALARM, ix-1)#define SENSOR_ATTR_PWM(ix) \ SENSOR_ATTR_2(pwm##ix, S_IRUGO, \ show_pwm, NULL, SHOW_PWM, ix-1), \ SENSOR_ATTR_2(pwm##ix##_enable, S_IRUGO | S_IWUSR, \ show_pwm, set_pwm, SHOW_SET_PWM_ENABLE, ix-1), \ SENSOR_ATTR_2(pwm##ix##_auto_channels_temp, S_IRUGO | S_IWUSR, \ show_pwm, set_pwm, SHOW_SET_PWM_AUTO_CHANNELS_TEMP, ix-1)#define SENSOR_ATTR_PWM_FREQ(ix) \ SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO | S_IWUSR, \ show_pwm, set_pwm, SHOW_SET_PWM_FREQ, ix-1)#define SENSOR_ATTR_PWM_FREQ_RO(ix) \ SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO, \ show_pwm, NULL, SHOW_SET_PWM_FREQ, ix-1)#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP(ix, ap) \ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO | S_IWUSR, \ show_pwm_auto_point_temp, set_pwm_auto_point_temp, \ ap-1, ix-1)#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(ix, ap) \ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO, \ show_pwm_auto_point_temp, NULL, \ ap-1, ix-1)#define SENSOR_ATTR_PWM_AUTO_POINT_PWM(ix, ap) \ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO | S_IWUSR, \ show_pwm_auto_point_pwm, set_pwm_auto_point_pwm, \ ap-1, ix-1)#define SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(ix, ap) \ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO, \ show_pwm_auto_point_pwm, NULL, \ ap-1, ix-1)static struct sensor_device_attribute_2 vt1211_sysfs_fan_pwm[] = { SENSOR_ATTR_FAN(1), SENSOR_ATTR_FAN(2), SENSOR_ATTR_PWM(1), SENSOR_ATTR_PWM(2), SENSOR_ATTR_PWM_FREQ(1), SENSOR_ATTR_PWM_FREQ_RO(2), SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 1), SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 2), SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 3), SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 4), SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 1), SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 2), SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 3), SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 4), SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 1), SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 2), SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 3), SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 4), SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 1), SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 2), SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 3), SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 4),};static struct device_attribute vt1211_sysfs_misc[] = { __ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm), __ATTR(cpu0_vid, S_IRUGO, show_vid, NULL), __ATTR(name, S_IRUGO, show_name, NULL), __ATTR(alarms, S_IRUGO, show_alarms, NULL),};/* --------------------------------------------------------------------- * Device registration and initialization * --------------------------------------------------------------------- */static void __devinit vt1211_init_device(struct vt1211_data *data){ /* set VRM */ data->vrm = vid_which_vrm(); /* Read (and initialize) UCH config */ data->uch_config = vt1211_read8(data, VT1211_REG_UCH_CONFIG); if (uch_config > -1) { data->uch_config = (data->uch_config & 0x83) | (uch_config << 2); vt1211_write8(data, VT1211_REG_UCH_CONFIG, data->uch_config); } /* 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;}static void vt1211_remove_sysfs(struct platform_device *pdev){ struct device *dev = &pdev->dev; int i; for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) { device_remove_file(dev, &vt1211_sysfs_in_input[i].dev_attr); device_remove_file(dev, &vt1211_sysfs_in_min[i].dev_attr); device_remove_file(dev, &vt1211_sysfs_in_max[i].dev_attr); device_remove_file(dev, &vt1211_sysfs_in_alarm[i].dev_attr); } for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) { device_remove_file(dev, &vt1211_sysfs_temp_input[i].dev_attr); device_remove_file(dev, &vt1211_sysfs_temp_max[i].dev_attr); device_remove_file(dev, &vt1211_sysfs_temp_max_hyst[i].dev_attr); device_remove_file(dev, &vt1211_sysfs_temp_alarm[i].dev_attr); } 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]); }}static int __devinit vt1211_probe(struct platform_device *pdev){ struct device *dev = &pdev->dev; struct vt1211_data *data; struct resource *res; int i, err; if (!(data = kzalloc(sizeof(struct vt1211_data), GFP_KERNEL))) { err = -ENOMEM; dev_err(dev, "Out of memory\n"); goto EXIT; } res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!request_region(res->start, res->end - res->start + 1, DRVNAME)) { err = -EBUSY; dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", (unsigned long)res->start, (unsigned long)res->end); goto EXIT_KFREE; } 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_sysfs_in_input); i++) { if (ISVOLT(i, data->uch_config)) { if ((err = device_create_file(dev, &vt1211_sysfs_in_input[i].dev_attr)) || (err = device_create_file(dev, &vt1211_sysfs_in_min[i].dev_attr)) || (err = device_create_file(dev, &vt1211_sysfs_in_max[i].dev_attr)) || (err = device_create_file(dev, &vt1211_sysfs_in_alarm[i].dev_attr))) { goto EXIT_DEV_REMOVE; } } } for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) { if (ISTEMP(i, data->uch_config)) { if ((err = device_create_file(dev, &vt1211_sysfs_temp_input[i].dev_attr)) || (err = device_create_file(dev, &vt1211_sysfs_temp_max[i].dev_attr)) || (err = device_create_file(dev, &vt1211_sysfs_temp_max_hyst[i].dev_attr)) || (err = device_create_file(dev, &vt1211_sysfs_temp_alarm[i].dev_attr))) { 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; } } /* Register device */ data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); dev_err(dev, "Class registration failed (%d)\n", err); goto EXIT_DEV_REMOVE_SILENT; } return 0;EXIT_DEV_REMOVE: dev_err(dev, "Sysfs interface creation failed (%d)\n", err);EXIT_DEV_REMOVE_SILENT: vt1211_remove_sysfs(pdev); release_region(res->start, res->end - res->start + 1);EXIT_KFREE: platform_set_drvdata(pdev, NULL); kfree(data);EXIT: return err;}static int __devexit vt1211_remove(struct platform_device *pdev){ struct vt1211_data *data = platform_get_drvdata(pdev); struct resource *res; hwmon_device_unregister(data->hwmon_dev); vt1211_remove_sysfs(pdev); platform_set_drvdata(pdev, NULL); kfree(data); res = platform_get_resource(pdev, IORESOURCE_IO, 0); release_region(res->start, res->end - res->start + 1); return 0;}static struct platform_driver vt1211_driver = { .driver = { .owner = THIS_MODULE, .name = DRVNAME, }, .probe = vt1211_probe, .remove = __devexit_p(vt1211_remove),};static int __init vt1211_device_add(unsigned short address){ struct resource res = { .start = address, .end = address + 0x7f, .flags = IORESOURCE_IO, }; int err; pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; printk(KERN_ERR DRVNAME ": Device allocation failed (%d)\n", err); goto EXIT; } res.name = pdev->name; err = platform_device_add_resources(pdev, &res, 1); if (err) { printk(KERN_ERR DRVNAME ": Device resource addition failed " "(%d)\n", err); goto EXIT_DEV_PUT; } err = platform_device_add(pdev); if (err) { printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", err); goto EXIT_DEV_PUT; } return 0;EXIT_DEV_PUT: platform_device_put(pdev);EXIT: return err;}static int __init vt1211_find(int sio_cip, unsigned short *address){ int err = -ENODEV; superio_enter(sio_cip); if (superio_inb(sio_cip, SIO_VT1211_DEVID) != SIO_VT1211_ID) { goto EXIT; } superio_select(sio_cip, SIO_VT1211_LDN_HWMON); if ((superio_inb(sio_cip, SIO_VT1211_ACTIVE) & 1) == 0) { printk(KERN_WARNING DRVNAME ": HW monitor is disabled, " "skipping\n"); goto EXIT; } *address = ((superio_inb(sio_cip, SIO_VT1211_BADDR) << 8) | (superio_inb(sio_cip, SIO_VT1211_BADDR + 1))) & 0xff00; if (*address == 0) { printk(KERN_WARNING DRVNAME ": Base address is not set, " "skipping\n"); goto EXIT; } err = 0; printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, " "revision %u\n", *address, superio_inb(sio_cip, SIO_VT1211_DEVREV));EXIT: superio_exit(sio_cip); return err;}static int __init vt1211_init(void){ int err; unsigned short address = 0; if ((err = vt1211_find(SIO_REG_CIP1, &address)) && (err = vt1211_find(SIO_REG_CIP2, &address))) { goto EXIT; } if ((uch_config < -1) || (uch_config > 31)) { err = -EINVAL; printk(KERN_WARNING DRVNAME ": Invalid UCH configuration %d. " "Choose a value between 0 and 31.\n", uch_config); goto EXIT; } if ((int_mode < -1) || (int_mode > 0)) { err = -EINVAL; printk(KERN_WARNING DRVNAME ": Invalid interrupt mode %d. " "Only mode 0 is supported.\n", int_mode); goto EXIT; } err = platform_driver_register(&vt1211_driver); if (err) { goto EXIT; } /* Sets global pdev as a side effect */ err = vt1211_device_add(address); if (err) { goto EXIT_DRV_UNREGISTER; } return 0;EXIT_DRV_UNREGISTER: platform_driver_unregister(&vt1211_driver);EXIT: return err;}static void __exit vt1211_exit(void){ platform_device_unregister(pdev); platform_driver_unregister(&vt1211_driver);}MODULE_AUTHOR("Juerg Haefliger <juergh@gmail.com>");MODULE_DESCRIPTION("VT1211 sensors");MODULE_LICENSE("GPL");module_init(vt1211_init);module_exit(vt1211_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?