therm_pm72.c
来自「linux 内核源代码」· C语言 代码 · 共 2,259 行 · 第 1/5 页
C
2,259 行
start = jiffies; down(&driver_lock); /* Tickle the FCU just in case */ if (--fcu_tickle_ticks < 0) { fcu_tickle_ticks = FCU_TICKLE_TICKS; tickle_fcu(); } /* First, we always calculate the new DIMMs state on an Xserve */ if (rackmac) do_monitor_dimms(&dimms_state); /* Then, the CPUs */ if (cpu_pid_type == CPU_PID_TYPE_COMBINED) do_monitor_cpu_combined(); else if (cpu_pid_type == CPU_PID_TYPE_RACKMAC) { do_monitor_cpu_rack(&cpu_state[0]); if (cpu_state[1].monitor != NULL) do_monitor_cpu_rack(&cpu_state[1]); // better deal with UP } else { do_monitor_cpu_split(&cpu_state[0]); if (cpu_state[1].monitor != NULL) do_monitor_cpu_split(&cpu_state[1]); // better deal with UP } /* Then, the rest */ do_monitor_backside(&backside_state); if (rackmac) do_monitor_slots(&slots_state); else 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 elapsed = jiffies - start; if (elapsed < HZ) schedule_timeout_interruptible(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); dispose_slots_state(&slots_state); dispose_dimms_state(&dimms_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); /* Decide the type of PID algorithm to use based on the presence of * the pumps, though that may not be the best way, that is good enough * for now */ if (rackmac) cpu_pid_type = CPU_PID_TYPE_RACKMAC; else if (machine_is_compatible("PowerMac7,3") && (cpu_count > 1) && fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID && fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) { printk(KERN_INFO "Liquid cooling pumps detected, using new algorithm !\n"); cpu_pid_type = CPU_PID_TYPE_COMBINED; } else cpu_pid_type = CPU_PID_TYPE_SPLIT; /* Create control loops for everything. If any fail, everything * fails */ if (init_cpu_state(&cpu_state[0], 0)) goto fail; if (cpu_pid_type == CPU_PID_TYPE_COMBINED) fetch_cpu_pumps_minmax(); if (cpu_count > 1 && init_cpu_state(&cpu_state[1], 1)) goto fail; if (init_backside_state(&backside_state)) goto fail; if (rackmac && init_dimms_state(&dimms_state)) goto fail; if (rackmac && init_slots_state(&slots_state)) goto fail; if (!rackmac && 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\n"); if (k2 || !rackmac) 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; } else if (k2 == NULL && !strcmp(adapter->name, "mac-io 0")) { k2 = adapter; DBG("Found K2\n"); if (u3_0 && rackmac) if (create_control_loops()) k2 = NULL; } /* We got all we need, start control loops */ if (u3_0 != NULL && u3_1 != NULL && (k2 || !rackmac)) { 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 fan_check_loc_match(const char *loc, int fan){ char tmp[64]; char *c, *e; strlcpy(tmp, fcu_fans[fan].loc, 64); c = tmp; for (;;) { e = strchr(c, ','); if (e) *e = 0; if (strcmp(loc, c) == 0) return 1; if (e == NULL) break; c = e + 1; } return 0;}static void fcu_lookup_fans(struct device_node *fcu_node){ struct device_node *np = NULL; int i; /* The table is filled by default with values that are suitable * for the old machines without device-tree informations. We scan * the device-tree and override those values with whatever is * there */ DBG("Looking up FCU controls in device-tree...\n"); while ((np = of_get_next_child(fcu_node, np)) != NULL) { int type = -1; const char *loc; const u32 *reg; DBG(" control: %s, type: %s\n", np->name, np->type); /* Detect control type */ if (!strcmp(np->type, "fan-rpm-control") || !strcmp(np->type, "fan-rpm")) type = FCU_FAN_RPM; if (!strcmp(np->type, "fan-pwm-control") || !strcmp(np->type, "fan-pwm")) type = FCU_FAN_PWM; /* Only care about fans for now */ if (type == -1) continue; /* Lookup for a matching location */ loc = of_get_property(np, "location", NULL); reg = of_get_property(np, "reg", NULL); if (loc == NULL || reg == NULL) continue; DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg); for (i = 0; i < FCU_FAN_COUNT; i++) { int fan_id; if (!fan_check_loc_match(loc, i)) continue; DBG(" location match, index: %d\n", i); fcu_fans[i].id = FCU_FAN_ABSENT_ID; if (type != fcu_fans[i].type) { printk(KERN_WARNING "therm_pm72: Fan type mismatch " "in device-tree for %s\n", np->full_name); break; } if (type == FCU_FAN_RPM) fan_id = ((*reg) - 0x10) / 2; else fan_id = ((*reg) - 0x30) / 2; if (fan_id > 7) { printk(KERN_WARNING "therm_pm72: Can't parse " "fan ID in device-tree for %s\n", np->full_name); break; } DBG(" fan id -> %d, type -> %d\n", fan_id, type); fcu_fans[i].id = fan_id; } } /* Now dump the array */ printk(KERN_INFO "Detected fan controls:\n"); for (i = 0; i < FCU_FAN_COUNT; i++) { if (fcu_fans[i].id == FCU_FAN_ABSENT_ID) continue; printk(KERN_INFO " %d: %s fan, id %d, location: %s\n", i, fcu_fans[i].type == FCU_FAN_RPM ? "RPM" : "PWM", fcu_fans[i].id, fcu_fans[i].loc); }}static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match){ state = state_detached; /* Lookup the fans in the device tree */ fcu_lookup_fans(dev->node); /* Add the driver */ return i2c_add_driver(&therm_pm72_driver);}static int fcu_of_remove(struct of_device* dev){ i2c_del_driver(&therm_pm72_driver); return 0;}static struct of_device_id fcu_match[] = { { .type = "fcu", }, {},};static struct of_platform_driver fcu_of_platform_driver = { .name = "temperature", .match_table = fcu_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; rackmac = machine_is_compatible("RackMac3,1"); if (!machine_is_compatible("PowerMac7,2") && !machine_is_compatible("PowerMac7,3") && !rackmac) return -ENODEV; printk(KERN_INFO "PowerMac G5 Thermal control driver %s\n", VERSION); np = of_find_node_by_type(NULL, "fcu"); if (np == NULL) { /* Some machines have strangely broken device-tree */ np = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/fan@15e"); if (np == NULL) { printk(KERN_ERR "Can't find FCU in device-tree !\n"); return -ENODEV; } } of_dev = of_platform_device_create(np, "temperature", NULL); if (of_dev == NULL) { printk(KERN_ERR "Can't register FCU platform device !\n"); return -ENODEV; } of_register_platform_driver(&fcu_of_platform_driver); return 0;}static void __exit therm_pm72_exit(void){ of_unregister_platform_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 PowerMac G5 thermal control");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?