therm_pm72.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,340 行 · 第 1/3 页
C
1,340 行
return; state->ticks = DRIVES_PID_INTERVAL; DBG("drives:\n"); /* Check fan status */ rc = get_rpm_fan(DRIVES_FAN_RPM_ID, !RPM_PID_USE_ACTUAL_SPEED); if (rc < 0) { printk(KERN_WARNING "Error %d reading drives fan !\n", rc); /* XXX What do we do now ? */ } else state->rpm = rc; DBG(" current rpm: %d\n", state->rpm); /* Get some sensor readings */ temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor, DS1775_TEMP)) << 8; state->last_temp = temp; DBG(" temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp), FIX32TOPRINT(DRIVES_PID_INPUT_TARGET)); /* Store temperature and error in history array */ state->cur_sample = (state->cur_sample + 1) % DRIVES_PID_HISTORY_SIZE; state->sample_history[state->cur_sample] = temp; state->error_history[state->cur_sample] = temp - DRIVES_PID_INPUT_TARGET; /* If first loop, fill the history table */ if (state->first) { for (i = 0; i < (DRIVES_PID_HISTORY_SIZE - 1); i++) { state->cur_sample = (state->cur_sample + 1) % DRIVES_PID_HISTORY_SIZE; state->sample_history[state->cur_sample] = temp; state->error_history[state->cur_sample] = temp - DRIVES_PID_INPUT_TARGET; } state->first = 0; } /* Calculate the integral term */ sum = 0; integral = 0; for (i = 0; i < DRIVES_PID_HISTORY_SIZE; i++) integral += state->error_history[i]; integral *= DRIVES_PID_INTERVAL; DBG(" integral: %08x\n", integral); integ_p = ((s64)DRIVES_PID_G_r) * (s64)integral; DBG(" integ_p: %d\n", (int)(integ_p >> 36)); sum += integ_p; /* Calculate the derivative term */ derivative = state->error_history[state->cur_sample] - state->error_history[(state->cur_sample + DRIVES_PID_HISTORY_SIZE - 1) % DRIVES_PID_HISTORY_SIZE]; derivative /= DRIVES_PID_INTERVAL; deriv_p = ((s64)DRIVES_PID_G_d) * (s64)derivative; DBG(" deriv_p: %d\n", (int)(deriv_p >> 36)); sum += deriv_p; /* Calculate the proportional term */ prop_p = ((s64)DRIVES_PID_G_p) * (s64)(state->error_history[state->cur_sample]); DBG(" prop_p: %d\n", (int)(prop_p >> 36)); sum += prop_p; /* Scale sum */ sum >>= 36; DBG(" sum: %d\n", (int)sum); state->rpm += (s32)sum; if (state->rpm < DRIVES_PID_OUTPUT_MIN) state->rpm = DRIVES_PID_OUTPUT_MIN; if (state->rpm > DRIVES_PID_OUTPUT_MAX) state->rpm = DRIVES_PID_OUTPUT_MAX; DBG("** DRIVES RPM: %d\n", (int)state->rpm); set_rpm_fan(DRIVES_FAN_RPM_ID, state->rpm);}/* * Initialize the state structure for the drives bay fan control loop */static int init_drives_state(struct drives_pid_state *state){ state->ticks = 1; state->first = 1; state->rpm = 1000; state->monitor = attach_i2c_chip(DRIVES_DALLAS_ID, "drives_temp"); if (state->monitor == NULL) return -ENODEV; device_create_file(&of_dev->dev, &dev_attr_drives_temperature); device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm); return 0;}/* * Dispose of the state data for the drives control loop */static void dispose_drives_state(struct drives_pid_state *state){ if (state->monitor == NULL) return; device_remove_file(&of_dev->dev, &dev_attr_drives_temperature); device_remove_file(&of_dev->dev, &dev_attr_drives_fan_rpm); detach_i2c_chip(state->monitor); state->monitor = NULL;}static int call_critical_overtemp(void){ char *argv[] = { critical_overtemp_path, NULL }; static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; return call_usermodehelper(critical_overtemp_path, argv, envp, 0);}/* * Here's the kernel thread that calls the various control loops */static int main_control_loop(void *x){ daemonize("kfand"); DBG("main_control_loop started\n"); down(&driver_lock); if (start_fcu() < 0) { printk(KERN_ERR "kfand: failed to start FCU\n"); up(&driver_lock); goto out; } /* Set the PCI fan once for now */ set_pwm_fan(SLOTS_FAN_PWM_ID, SLOTS_FAN_DEFAULT_PWM); /* Initialize ADCs */ initialize_adc(&cpu_state[0]); if (cpu_state[1].monitor != NULL) initialize_adc(&cpu_state[1]); up(&driver_lock); while (state == state_attached) { unsigned long elapsed, start; start = jiffies; down(&driver_lock); do_monitor_cpu(&cpu_state[0]); if (cpu_state[1].monitor != NULL) do_monitor_cpu(&cpu_state[1]); do_monitor_backside(&backside_state); do_monitor_drives(&drives_state); up(&driver_lock); if (critical_state == 1) { printk(KERN_WARNING "Temperature control detected a critical condition\n"); printk(KERN_WARNING "Attempting to shut down...\n"); if (call_critical_overtemp()) { printk(KERN_WARNING "Can't call %s, power off now!\n", critical_overtemp_path); machine_power_off(); } } if (critical_state > 0) critical_state++; if (critical_state > MAX_CRITICAL_STATE) { printk(KERN_WARNING "Shutdown timed out, power off now !\n"); machine_power_off(); } // FIXME: Deal with signals set_current_state(TASK_INTERRUPTIBLE); elapsed = jiffies - start; if (elapsed < HZ) schedule_timeout(HZ - elapsed); } out: DBG("main_control_loop ended\n"); ctrl_task = 0; complete_and_exit(&ctrl_complete, 0);}/* * Dispose the control loops when tearing down */static void dispose_control_loops(void){ dispose_cpu_state(&cpu_state[0]); dispose_cpu_state(&cpu_state[1]); dispose_backside_state(&backside_state); dispose_drives_state(&drives_state);}/* * Create the control loops. U3-0 i2c bus is up, so we can now * get to the various sensors */static int create_control_loops(void){ struct device_node *np; /* Count CPUs from the device-tree, we don't care how many are * actually used by Linux */ cpu_count = 0; for (np = NULL; NULL != (np = of_find_node_by_type(np, "cpu"));) cpu_count++; DBG("counted %d CPUs in the device-tree\n", cpu_count); /* Create control loops for everything. If any fail, everything * fails */ if (init_cpu_state(&cpu_state[0], 0)) goto fail; if (cpu_count > 1 && init_cpu_state(&cpu_state[1], 1)) goto fail; if (init_backside_state(&backside_state)) goto fail; if (init_drives_state(&drives_state)) goto fail; DBG("all control loops up !\n"); return 0; fail: DBG("failure creating control loops, disposing\n"); dispose_control_loops(); return -ENODEV;}/* * Start the control loops after everything is up, that is create * the thread that will make them run */static void start_control_loops(void){ init_completion(&ctrl_complete); ctrl_task = kernel_thread(main_control_loop, NULL, SIGCHLD | CLONE_KERNEL);}/* * Stop the control loops when tearing down */static void stop_control_loops(void){ if (ctrl_task != 0) wait_for_completion(&ctrl_complete);}/* * Attach to the i2c FCU after detecting U3-1 bus */static int attach_fcu(void){ fcu = attach_i2c_chip(FAN_CTRLER_ID, "fcu"); if (fcu == NULL) return -ENODEV; DBG("FCU attached\n"); return 0;}/* * Detach from the i2c FCU when tearing down */static void detach_fcu(void){ if (fcu) detach_i2c_chip(fcu); fcu = NULL;}/* * Attach to the i2c controller. We probe the various chips based * on the device-tree nodes and build everything for the driver to * run, we then kick the driver monitoring thread */static int therm_pm72_attach(struct i2c_adapter *adapter){ down(&driver_lock); /* Check state */ if (state == state_detached) state = state_attaching; if (state != state_attaching) { up(&driver_lock); return 0; } /* Check if we are looking for one of these */ if (u3_0 == NULL && !strcmp(adapter->name, "u3 0")) { u3_0 = adapter; DBG("found U3-0, creating control loops\n"); if (create_control_loops()) u3_0 = NULL; } else if (u3_1 == NULL && !strcmp(adapter->name, "u3 1")) { u3_1 = adapter; DBG("found U3-1, attaching FCU\n"); if (attach_fcu()) u3_1 = NULL; } /* We got all we need, start control loops */ if (u3_0 != NULL && u3_1 != NULL) { DBG("everything up, starting control loops\n"); state = state_attached; start_control_loops(); } up(&driver_lock); return 0;}/* * Called on every adapter when the driver or the i2c controller * is going away. */static int therm_pm72_detach(struct i2c_adapter *adapter){ down(&driver_lock); if (state != state_detached) state = state_detaching; /* Stop control loops if any */ DBG("stopping control loops\n"); up(&driver_lock); stop_control_loops(); down(&driver_lock); if (u3_0 != NULL && !strcmp(adapter->name, "u3 0")) { DBG("lost U3-0, disposing control loops\n"); dispose_control_loops(); u3_0 = NULL; } if (u3_1 != NULL && !strcmp(adapter->name, "u3 1")) { DBG("lost U3-1, detaching FCU\n"); detach_fcu(); u3_1 = NULL; } if (u3_0 == NULL && u3_1 == NULL) state = state_detached; up(&driver_lock); return 0;}static int fcu_of_probe(struct of_device* dev, const struct of_match *match){ int rc; state = state_detached; rc = i2c_add_driver(&therm_pm72_driver); if (rc < 0) return rc; return 0;}static int fcu_of_remove(struct of_device* dev){ i2c_del_driver(&therm_pm72_driver); return 0;}static struct of_match fcu_of_match[] = { { .name = OF_ANY_MATCH, .type = "fcu", .compatible = OF_ANY_MATCH }, {},};static struct of_platform_driver fcu_of_platform_driver = { .name = "temperature", .match_table = fcu_of_match, .probe = fcu_of_probe, .remove = fcu_of_remove};/* * Check machine type, attach to i2c controller */static int __init therm_pm72_init(void){ struct device_node *np; if (!machine_is_compatible("PowerMac7,2")) return -ENODEV; printk(KERN_INFO "PowerMac G5 Thermal control driver %s\n", VERSION); np = of_find_node_by_type(NULL, "fcu"); if (np == NULL) { printk(KERN_ERR "Can't find FCU in device-tree !\n"); return -ENODEV; } of_dev = of_platform_device_create(np, "temperature"); if (of_dev == NULL) { printk(KERN_ERR "Can't register FCU platform device !\n"); return -ENODEV; } of_register_driver(&fcu_of_platform_driver); return 0;}static void __exit therm_pm72_exit(void){ of_unregister_driver(&fcu_of_platform_driver); if (of_dev) of_device_unregister(of_dev);}module_init(therm_pm72_init);module_exit(therm_pm72_exit);MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");MODULE_DESCRIPTION("Driver for Apple's PowerMac7,2 G5 thermal control");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?