smsc47m1.c
来自「linux 内核源代码」· C语言 代码 · 共 759 行 · 第 1/2 页
C
759 行
&sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_pwm2_enable.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, &dev_attr_alarms.attr, &dev_attr_name.attr, NULL};static const struct attribute_group smsc47m1_group = { .attrs = smsc47m1_attributes,};static int __init smsc47m1_find(unsigned short *addr, struct smsc47m1_sio_data *sio_data){ u8 val; superio_enter(); val = superio_inb(SUPERIO_REG_DEVID); /* * SMSC LPC47M10x/LPC47M112/LPC47M13x (device id 0x59), LPC47M14x * (device id 0x5F) and LPC47B27x (device id 0x51) have fan control. * The LPC47M15x and LPC47M192 chips "with hardware monitoring block" * can do much more besides (device id 0x60). * The LPC47M997 is undocumented, but seems to be compatible with * the LPC47M192, and has the same device id. * The LPC47M292 (device id 0x6B) is somewhat compatible, but it * supports a 3rd fan, and the pin configuration registers are * unfortunately different. */ switch (val) { case 0x51: pr_info(DRVNAME ": Found SMSC LPC47B27x\n"); sio_data->type = smsc47m1; break; case 0x59: pr_info(DRVNAME ": Found SMSC LPC47M10x/LPC47M112/LPC47M13x\n"); sio_data->type = smsc47m1; break; case 0x5F: pr_info(DRVNAME ": Found SMSC LPC47M14x\n"); sio_data->type = smsc47m1; break; case 0x60: pr_info(DRVNAME ": Found SMSC LPC47M15x/LPC47M192/LPC47M997\n"); sio_data->type = smsc47m1; break; case 0x6B: pr_info(DRVNAME ": Found SMSC LPC47M292\n"); sio_data->type = smsc47m2; break; default: superio_exit(); return -ENODEV; } superio_select(); *addr = (superio_inb(SUPERIO_REG_BASE) << 8) | superio_inb(SUPERIO_REG_BASE + 1); val = superio_inb(SUPERIO_REG_ACT); if (*addr == 0 || (val & 0x01) == 0) { pr_info(DRVNAME ": Device is disabled, will not use\n"); superio_exit(); return -ENODEV; } superio_exit(); return 0;}static int __devinit smsc47m1_probe(struct platform_device *pdev){ struct device *dev = &pdev->dev; struct smsc47m1_sio_data *sio_data = dev->platform_data; struct smsc47m1_data *data; struct resource *res; int err = 0; int fan1, fan2, fan3, pwm1, pwm2, pwm3; static const char *names[] = { "smsc47m1", "smsc47m2", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!request_region(res->start, SMSC_EXTENT, DRVNAME)) { dev_err(dev, "Region 0x%lx-0x%lx already in use!\n", (unsigned long)res->start, (unsigned long)res->end); return -EBUSY; } if (!(data = kzalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { err = -ENOMEM; goto error_release; } data->addr = res->start; data->type = sio_data->type; data->name = names[sio_data->type]; mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); /* If no function is properly configured, there's no point in actually registering the chip. */ pwm1 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(0)) & 0x05) == 0x04; pwm2 = (smsc47m1_read_value(data, SMSC47M1_REG_PPIN(1)) & 0x05) == 0x04; if (data->type == smsc47m2) { fan1 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN1) & 0x0d) == 0x09; fan2 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN2) & 0x0d) == 0x09; fan3 = (smsc47m1_read_value(data, SMSC47M2_REG_TPIN3) & 0x0d) == 0x0d; pwm3 = (smsc47m1_read_value(data, SMSC47M2_REG_PPIN3) & 0x0d) == 0x08; } else { fan1 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(0)) & 0x05) == 0x05; fan2 = (smsc47m1_read_value(data, SMSC47M1_REG_TPIN(1)) & 0x05) == 0x05; fan3 = 0; pwm3 = 0; } if (!(fan1 || fan2 || fan3 || pwm1 || pwm2 || pwm3)) { dev_warn(dev, "Device not configured, will not use\n"); err = -ENODEV; goto error_free; } /* Some values (fan min, clock dividers, pwm registers) may be needed before any update is triggered, so we better read them at least once here. We don't usually do it that way, but in this particular case, manually reading 5 registers out of 8 doesn't make much sense and we're better using the existing function. */ smsc47m1_update_device(dev, 1); /* Register sysfs hooks */ if (fan1) { if ((err = device_create_file(dev, &sensor_dev_attr_fan1_input.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_fan1_min.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_fan1_div.dev_attr))) goto error_remove_files; } else dev_dbg(dev, "Fan 1 not enabled by hardware, skipping\n"); if (fan2) { if ((err = device_create_file(dev, &sensor_dev_attr_fan2_input.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_fan2_min.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_fan2_div.dev_attr))) goto error_remove_files; } else dev_dbg(dev, "Fan 2 not enabled by hardware, skipping\n"); if (fan3) { if ((err = device_create_file(dev, &sensor_dev_attr_fan3_input.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_fan3_min.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_fan3_div.dev_attr))) goto error_remove_files; } else if (data->type == smsc47m2) dev_dbg(dev, "Fan 3 not enabled by hardware, skipping\n"); if (pwm1) { if ((err = device_create_file(dev, &sensor_dev_attr_pwm1.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_pwm1_enable.dev_attr))) goto error_remove_files; } else dev_dbg(dev, "PWM 1 not enabled by hardware, skipping\n"); if (pwm2) { if ((err = device_create_file(dev, &sensor_dev_attr_pwm2.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_pwm2_enable.dev_attr))) goto error_remove_files; } else dev_dbg(dev, "PWM 2 not enabled by hardware, skipping\n"); if (pwm3) { if ((err = device_create_file(dev, &sensor_dev_attr_pwm3.dev_attr)) || (err = device_create_file(dev, &sensor_dev_attr_pwm3_enable.dev_attr))) goto error_remove_files; } else if (data->type == smsc47m2) dev_dbg(dev, "PWM 3 not enabled by hardware, skipping\n"); if ((err = device_create_file(dev, &dev_attr_alarms))) goto error_remove_files; if ((err = device_create_file(dev, &dev_attr_name))) goto error_remove_files; data->hwmon_dev = hwmon_device_register(dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); goto error_remove_files; } return 0;error_remove_files: sysfs_remove_group(&dev->kobj, &smsc47m1_group);error_free: platform_set_drvdata(pdev, NULL); kfree(data);error_release: release_region(res->start, SMSC_EXTENT); return err;}static int __devexit smsc47m1_remove(struct platform_device *pdev){ struct smsc47m1_data *data = platform_get_drvdata(pdev); struct resource *res; hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&pdev->dev.kobj, &smsc47m1_group); res = platform_get_resource(pdev, IORESOURCE_IO, 0); release_region(res->start, SMSC_EXTENT); platform_set_drvdata(pdev, NULL); kfree(data); return 0;}static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, int init){ struct smsc47m1_data *data = dev_get_drvdata(dev); mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) { int i, fan_nr; fan_nr = data->type == smsc47m2 ? 3 : 2; for (i = 0; i < fan_nr; i++) { data->fan[i] = smsc47m1_read_value(data, SMSC47M1_REG_FAN[i]); data->fan_preload[i] = smsc47m1_read_value(data, SMSC47M1_REG_FAN_PRELOAD[i]); data->pwm[i] = smsc47m1_read_value(data, SMSC47M1_REG_PWM[i]); } i = smsc47m1_read_value(data, SMSC47M1_REG_FANDIV); data->fan_div[0] = (i >> 4) & 0x03; data->fan_div[1] = i >> 6; data->alarms = smsc47m1_read_value(data, SMSC47M1_REG_ALARM) >> 6; /* Clear alarms if needed */ if (data->alarms) smsc47m1_write_value(data, SMSC47M1_REG_ALARM, 0xC0); if (fan_nr >= 3) { data->fan_div[2] = (smsc47m1_read_value(data, SMSC47M2_REG_FANDIV3) >> 4) & 0x03; data->alarms |= (smsc47m1_read_value(data, SMSC47M2_REG_ALARM6) & 0x40) >> 4; /* Clear alarm if needed */ if (data->alarms & 0x04) smsc47m1_write_value(data, SMSC47M2_REG_ALARM6, 0x40); } data->last_updated = jiffies; } mutex_unlock(&data->update_lock); return data;}static int __init smsc47m1_device_add(unsigned short address, const struct smsc47m1_sio_data *sio_data){ struct resource res = { .start = address, .end = address + SMSC_EXTENT - 1, .name = DRVNAME, .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; } 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 smsc47m1_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 sm_smsc47m1_init(void){ int err; unsigned short address; struct smsc47m1_sio_data sio_data; if (smsc47m1_find(&address, &sio_data)) return -ENODEV; err = platform_driver_register(&smsc47m1_driver); if (err) goto exit; /* Sets global pdev as a side effect */ err = smsc47m1_device_add(address, &sio_data); if (err) goto exit_driver; return 0;exit_driver: platform_driver_unregister(&smsc47m1_driver);exit: return err;}static void __exit sm_smsc47m1_exit(void){ platform_device_unregister(pdev); platform_driver_unregister(&smsc47m1_driver);}MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver");MODULE_LICENSE("GPL");module_init(sm_smsc47m1_init);module_exit(sm_smsc47m1_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?