📄 f71805f.c
字号:
&sensor_dev_attr_in1_alarm.dev_attr.attr, &sensor_dev_attr_in2_alarm.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr, &sensor_dev_attr_in5_alarm.dev_attr.attr, &sensor_dev_attr_in6_alarm.dev_attr.attr, &sensor_dev_attr_in7_alarm.dev_attr.attr, &dev_attr_alarms_in.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr, &sensor_dev_attr_temp3_alarm.dev_attr.attr, &dev_attr_alarms_temp.attr, &dev_attr_alarms_fan.attr, &dev_attr_name.attr, NULL};static const struct attribute_group f71805f_group = { .attrs = f71805f_attributes,};static struct attribute *f71805f_attributes_optin[4][5] = { { &sensor_dev_attr_in4_input.dev_attr.attr, &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, NULL }, { &sensor_dev_attr_in8_input.dev_attr.attr, &sensor_dev_attr_in8_max.dev_attr.attr, &sensor_dev_attr_in8_min.dev_attr.attr, &sensor_dev_attr_in8_alarm.dev_attr.attr, NULL }, { &sensor_dev_attr_in9_input.dev_attr.attr, &sensor_dev_attr_in9_max.dev_attr.attr, &sensor_dev_attr_in9_min.dev_attr.attr, &sensor_dev_attr_in9_alarm.dev_attr.attr, NULL }, { &sensor_dev_attr_in10_input.dev_attr.attr, &sensor_dev_attr_in10_max.dev_attr.attr, &sensor_dev_attr_in10_min.dev_attr.attr, &sensor_dev_attr_in10_alarm.dev_attr.attr, NULL }};static const struct attribute_group f71805f_group_optin[4] = { { .attrs = f71805f_attributes_optin[0] }, { .attrs = f71805f_attributes_optin[1] }, { .attrs = f71805f_attributes_optin[2] }, { .attrs = f71805f_attributes_optin[3] },};/* We don't include pwm_freq files in the arrays above, because they must be created conditionally (only if pwm_mode is 1 == PWM) */static struct attribute *f71805f_attributes_pwm_freq[] = { &sensor_dev_attr_pwm1_freq.dev_attr.attr, &sensor_dev_attr_pwm2_freq.dev_attr.attr, &sensor_dev_attr_pwm3_freq.dev_attr.attr, NULL};static const struct attribute_group f71805f_group_pwm_freq = { .attrs = f71805f_attributes_pwm_freq,};/* We also need an indexed access to pwmN files to toggle writability */static struct attribute *f71805f_attr_pwm[] = { &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr,};/* * Device registration and initialization */static void __devinit f71805f_init_device(struct f71805f_data *data){ u8 reg; int i; reg = f71805f_read8(data, F71805F_REG_START); if ((reg & 0x41) != 0x01) { printk(KERN_DEBUG DRVNAME ": Starting monitoring " "operations\n"); f71805f_write8(data, F71805F_REG_START, (reg | 0x01) & ~0x40); } /* Fan monitoring can be disabled. If it is, we won't be polling the register values, and won't create the related sysfs files. */ for (i = 0; i < 3; i++) { data->fan_ctrl[i] = f71805f_read8(data, F71805F_REG_FAN_CTRL(i)); /* Clear latch full bit, else "speed mode" fan speed control doesn't work */ if (data->fan_ctrl[i] & FAN_CTRL_LATCH_FULL) { data->fan_ctrl[i] &= ~FAN_CTRL_LATCH_FULL; f71805f_write8(data, F71805F_REG_FAN_CTRL(i), data->fan_ctrl[i]); } }}static int __devinit f71805f_probe(struct platform_device *pdev){ struct f71805f_sio_data *sio_data = pdev->dev.platform_data; struct f71805f_data *data; struct resource *res; int i, err; static const char *names[] = { "f71805f", "f71872f", }; if (!(data = kzalloc(sizeof(struct f71805f_data), GFP_KERNEL))) { err = -ENOMEM; printk(KERN_ERR DRVNAME ": Out of memory\n"); goto exit; } res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!request_region(res->start + ADDR_REG_OFFSET, 2, DRVNAME)) { err = -EBUSY; dev_err(&pdev->dev, "Failed to request region 0x%lx-0x%lx\n", (unsigned long)(res->start + ADDR_REG_OFFSET), (unsigned long)(res->start + ADDR_REG_OFFSET + 1)); goto exit_free; } data->addr = res->start; data->name = names[sio_data->kind]; mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); /* Some voltage inputs depend on chip model and configuration */ switch (sio_data->kind) { case f71805f: data->has_in = 0x1ff; break; case f71872f: data->has_in = 0x6ef; if (sio_data->fnsel1 & 0x01) data->has_in |= (1 << 4); /* in4 */ if (sio_data->fnsel1 & 0x02) data->has_in |= (1 << 8); /* in8 */ break; } /* Initialize the F71805F chip */ f71805f_init_device(data); /* Register sysfs interface files */ if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group))) goto exit_release_region; if (data->has_in & (1 << 4)) { /* in4 */ if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group_optin[0]))) goto exit_remove_files; } if (data->has_in & (1 << 8)) { /* in8 */ if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group_optin[1]))) goto exit_remove_files; } if (data->has_in & (1 << 9)) { /* in9 (F71872F/FG only) */ if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group_optin[2]))) goto exit_remove_files; } if (data->has_in & (1 << 10)) { /* in9 (F71872F/FG only) */ if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group_optin[3]))) goto exit_remove_files; } for (i = 0; i < 3; i++) { /* If control mode is PWM, create pwm_freq file */ if (!(data->fan_ctrl[i] & FAN_CTRL_DC_MODE)) { if ((err = sysfs_create_file(&pdev->dev.kobj, f71805f_attributes_pwm_freq[i]))) goto exit_remove_files; } /* If PWM is in manual mode, add write permission */ if (data->fan_ctrl[i] & FAN_CTRL_MODE_MANUAL) { if ((err = sysfs_chmod_file(&pdev->dev.kobj, f71805f_attr_pwm[i], S_IRUGO | S_IWUSR))) { dev_err(&pdev->dev, "chmod +w pwm%d failed\n", i + 1); goto exit_remove_files; } } } data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); dev_err(&pdev->dev, "Class registration failed (%d)\n", err); goto exit_remove_files; } return 0;exit_remove_files: sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); for (i = 0; i < 4; i++) sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq);exit_release_region: release_region(res->start + ADDR_REG_OFFSET, 2);exit_free: platform_set_drvdata(pdev, NULL); kfree(data);exit: return err;}static int __devexit f71805f_remove(struct platform_device *pdev){ struct f71805f_data *data = platform_get_drvdata(pdev); struct resource *res; int i; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); for (i = 0; i < 4; i++) sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); platform_set_drvdata(pdev, NULL); kfree(data); res = platform_get_resource(pdev, IORESOURCE_IO, 0); release_region(res->start + ADDR_REG_OFFSET, 2); return 0;}static struct platform_driver f71805f_driver = { .driver = { .owner = THIS_MODULE, .name = DRVNAME, }, .probe = f71805f_probe, .remove = __devexit_p(f71805f_remove),};static int __init f71805f_device_add(unsigned short address, const struct f71805f_sio_data *sio_data){ struct resource res = { .start = address, .end = address + REGION_LENGTH - 1, .flags = IORESOURCE_IO, }; int err; pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; printk(KERN_ERR DRVNAME ": Device allocation failed\n"); 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_device_put; } err = platform_device_add_data(pdev, sio_data, sizeof(struct f71805f_sio_data)); if (err) { printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); goto exit_device_put; } err = platform_device_add(pdev); if (err) { printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", err); goto exit_device_put; } return 0;exit_device_put: platform_device_put(pdev);exit: return err;}static int __init f71805f_find(int sioaddr, unsigned short *address, struct f71805f_sio_data *sio_data){ int err = -ENODEV; u16 devid; static const char *names[] = { "F71805F/FG", "F71872F/FG or F71806F/FG", }; superio_enter(sioaddr); devid = superio_inw(sioaddr, SIO_REG_MANID); if (devid != SIO_FINTEK_ID) goto exit; devid = superio_inw(sioaddr, SIO_REG_DEVID); switch (devid) { case SIO_F71805F_ID: sio_data->kind = f71805f; break; case SIO_F71872F_ID: sio_data->kind = f71872f; sio_data->fnsel1 = superio_inb(sioaddr, SIO_REG_FNSEL1); break; default: printk(KERN_INFO DRVNAME ": Unsupported Fintek device, " "skipping\n"); goto exit; } superio_select(sioaddr, F71805F_LD_HWM); if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { printk(KERN_WARNING DRVNAME ": Device not activated, " "skipping\n"); goto exit; } *address = superio_inw(sioaddr, SIO_REG_ADDR); if (*address == 0) { printk(KERN_WARNING DRVNAME ": Base address not set, " "skipping\n"); goto exit; } *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ err = 0; printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %u\n", names[sio_data->kind], *address, superio_inb(sioaddr, SIO_REG_DEVREV));exit: superio_exit(sioaddr); return err;}static int __init f71805f_init(void){ int err; unsigned short address; struct f71805f_sio_data sio_data; if (f71805f_find(0x2e, &address, &sio_data) && f71805f_find(0x4e, &address, &sio_data)) return -ENODEV; err = platform_driver_register(&f71805f_driver); if (err) goto exit; /* Sets global pdev as a side effect */ err = f71805f_device_add(address, &sio_data); if (err) goto exit_driver; return 0;exit_driver: platform_driver_unregister(&f71805f_driver);exit: return err;}static void __exit f71805f_exit(void){ platform_device_unregister(pdev); platform_driver_unregister(&f71805f_driver);}MODULE_AUTHOR("Jean Delvare <khali@linux-fr>");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("F71805F/F71872F hardware monitoring driver");module_init(f71805f_init);module_exit(f71805f_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -