📄 time.c
字号:
} } /* * Do not try to get the alternate port aib if the clock * is not in sync yet. */ if (!eacr.es) return eacr; /* * If steai is available we can get the information about * the other port immediately. If only stetr is available the * data-port bit toggle has to be used. */ if (test_bit(ETR_FLAG_STEAI, &etr_flags)) { if (eacr.p0 && !etr_port0_uptodate) { etr_steai_cv(&etr_port0, ETR_STEAI_PORT_0); etr_port0_uptodate = 1; } if (eacr.p1 && !etr_port1_uptodate) { etr_steai_cv(&etr_port1, ETR_STEAI_PORT_1); etr_port1_uptodate = 1; } } else { /* * One port was updated above, if the other * port is not uptodate toggle dp bit. */ if ((eacr.p0 && !etr_port0_uptodate) || (eacr.p1 && !etr_port1_uptodate)) eacr.dp ^= 1; else eacr.dp = 0; } return eacr;}/* * Write new etr control register if it differs from the current one. * Return 1 if etr_tolec has been updated as well. */static void etr_update_eacr(struct etr_eacr eacr){ int dp_changed; if (memcmp(&etr_eacr, &eacr, sizeof(eacr)) == 0) /* No change, return. */ return; /* * The disable of an active port of the change of the data port * bit can/will cause a change in the data port. */ dp_changed = etr_eacr.e0 > eacr.e0 || etr_eacr.e1 > eacr.e1 || (etr_eacr.dp ^ eacr.dp) != 0; etr_eacr = eacr; etr_setr(&etr_eacr); if (dp_changed) etr_tolec = get_clock();}/* * ETR tasklet. In this function you'll find the main logic. In * particular this is the only function that calls etr_update_eacr(), * it "controls" the etr control register. */static void etr_work_fn(struct work_struct *work){ unsigned long long now; struct etr_eacr eacr; struct etr_aib aib; int sync_port; /* Create working copy of etr_eacr. */ eacr = etr_eacr; /* Check for the different events and their immediate effects. */ eacr = etr_handle_events(eacr); /* Check if ETR is supposed to be active. */ eacr.ea = eacr.p0 || eacr.p1; if (!eacr.ea) { /* Both ports offline. Reset everything. */ eacr.dp = eacr.es = eacr.sl = 0; on_each_cpu(etr_disable_sync_clock, NULL, 0, 1); del_timer_sync(&etr_timer); etr_update_eacr(eacr); set_bit(ETR_FLAG_EACCES, &etr_flags); return; } /* Store aib to get the current ETR status word. */ BUG_ON(etr_stetr(&aib) != 0); etr_port0.esw = etr_port1.esw = aib.esw; /* Copy status word. */ now = get_clock(); /* * Update the port information if the last stepping port change * or data port change is older than 1.6 seconds. */ if (now >= etr_tolec + (1600000 << 12)) eacr = etr_handle_update(&aib, eacr); /* * Select ports to enable. The prefered synchronization mode is PPS. * If a port can be enabled depends on a number of things: * 1) The port needs to be online and uptodate. A port is not * disabled just because it is not uptodate, but it is only * enabled if it is uptodate. * 2) The port needs to have the same mode (pps / etr). * 3) The port needs to be usable -> etr_port_valid() == 1 * 4) To enable the second port the clock needs to be in sync. * 5) If both ports are useable and are ETR ports, the network id * has to be the same. * The eacr.sl bit is used to indicate etr mode vs. pps mode. */ if (eacr.p0 && aib.esw.psc0 == etr_lpsc_pps_mode) { eacr.sl = 0; eacr.e0 = 1; if (!etr_mode_is_pps(etr_eacr)) eacr.es = 0; if (!eacr.es || !eacr.p1 || aib.esw.psc1 != etr_lpsc_pps_mode) eacr.e1 = 0; // FIXME: uptodate checks ? else if (etr_port0_uptodate && etr_port1_uptodate) eacr.e1 = 1; sync_port = (etr_port0_uptodate && etr_port_valid(&etr_port0, 0)) ? 0 : -1; clear_bit(ETR_FLAG_EACCES, &etr_flags); } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_pps_mode) { eacr.sl = 0; eacr.e0 = 0; eacr.e1 = 1; if (!etr_mode_is_pps(etr_eacr)) eacr.es = 0; sync_port = (etr_port1_uptodate && etr_port_valid(&etr_port1, 1)) ? 1 : -1; clear_bit(ETR_FLAG_EACCES, &etr_flags); } else if (eacr.p0 && aib.esw.psc0 == etr_lpsc_operational_step) { eacr.sl = 1; eacr.e0 = 1; if (!etr_mode_is_etr(etr_eacr)) eacr.es = 0; if (!eacr.es || !eacr.p1 || aib.esw.psc1 != etr_lpsc_operational_alt) eacr.e1 = 0; else if (etr_port0_uptodate && etr_port1_uptodate && etr_compare_network(&etr_port0, &etr_port1)) eacr.e1 = 1; sync_port = (etr_port0_uptodate && etr_port_valid(&etr_port0, 0)) ? 0 : -1; clear_bit(ETR_FLAG_EACCES, &etr_flags); } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_operational_step) { eacr.sl = 1; eacr.e0 = 0; eacr.e1 = 1; if (!etr_mode_is_etr(etr_eacr)) eacr.es = 0; sync_port = (etr_port1_uptodate && etr_port_valid(&etr_port1, 1)) ? 1 : -1; clear_bit(ETR_FLAG_EACCES, &etr_flags); } else { /* Both ports not usable. */ eacr.es = eacr.sl = 0; sync_port = -1; set_bit(ETR_FLAG_EACCES, &etr_flags); } /* * If the clock is in sync just update the eacr and return. * If there is no valid sync port wait for a port update. */ if (eacr.es || sync_port < 0) { etr_update_eacr(eacr); etr_set_tolec_timeout(now); return; } /* * Prepare control register for clock syncing * (reset data port bit, set sync check control. */ eacr.dp = 0; eacr.es = 1; /* * Update eacr and try to synchronize the clock. If the update * of eacr caused a stepping port switch (or if we have to * assume that a stepping port switch has occured) or the * clock syncing failed, reset the sync check control bit * and set up a timer to try again after 0.5 seconds */ etr_update_eacr(eacr); if (now < etr_tolec + (1600000 << 12) || etr_sync_clock(&aib, sync_port) != 0) { /* Sync failed. Try again in 1/2 second. */ eacr.es = 0; etr_update_eacr(eacr); etr_set_sync_timeout(); } else etr_set_tolec_timeout(now);}/* * Sysfs interface functions */static struct sysdev_class etr_sysclass = { set_kset_name("etr")};static struct sys_device etr_port0_dev = { .id = 0, .cls = &etr_sysclass,};static struct sys_device etr_port1_dev = { .id = 1, .cls = &etr_sysclass,};/* * ETR class attributes */static ssize_t etr_stepping_port_show(struct sysdev_class *class, char *buf){ return sprintf(buf, "%i\n", etr_port0.esw.p);}static SYSDEV_CLASS_ATTR(stepping_port, 0400, etr_stepping_port_show, NULL);static ssize_t etr_stepping_mode_show(struct sysdev_class *class, char *buf){ char *mode_str; if (etr_mode_is_pps(etr_eacr)) mode_str = "pps"; else if (etr_mode_is_etr(etr_eacr)) mode_str = "etr"; else mode_str = "local"; return sprintf(buf, "%s\n", mode_str);}static SYSDEV_CLASS_ATTR(stepping_mode, 0400, etr_stepping_mode_show, NULL);/* * ETR port attributes */static inline struct etr_aib *etr_aib_from_dev(struct sys_device *dev){ if (dev == &etr_port0_dev) return etr_port0_online ? &etr_port0 : NULL; else return etr_port1_online ? &etr_port1 : NULL;}static ssize_t etr_online_show(struct sys_device *dev, char *buf){ unsigned int online; online = (dev == &etr_port0_dev) ? etr_port0_online : etr_port1_online; return sprintf(buf, "%i\n", online);}static ssize_t etr_online_store(struct sys_device *dev, const char *buf, size_t count){ unsigned int value; value = simple_strtoul(buf, NULL, 0); if (value != 0 && value != 1) return -EINVAL; if (test_bit(ETR_FLAG_ENOSYS, &etr_flags)) return -ENOSYS; if (dev == &etr_port0_dev) { if (etr_port0_online == value) return count; /* Nothing to do. */ etr_port0_online = value; set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events); schedule_work(&etr_work); } else { if (etr_port1_online == value) return count; /* Nothing to do. */ etr_port1_online = value; set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events); schedule_work(&etr_work); } return count;}static SYSDEV_ATTR(online, 0600, etr_online_show, etr_online_store);static ssize_t etr_stepping_control_show(struct sys_device *dev, char *buf){ return sprintf(buf, "%i\n", (dev == &etr_port0_dev) ? etr_eacr.e0 : etr_eacr.e1);}static SYSDEV_ATTR(stepping_control, 0400, etr_stepping_control_show, NULL);static ssize_t etr_mode_code_show(struct sys_device *dev, char *buf){ if (!etr_port0_online && !etr_port1_online) /* Status word is not uptodate if both ports are offline. */ return -ENODATA; return sprintf(buf, "%i\n", (dev == &etr_port0_dev) ? etr_port0.esw.psc0 : etr_port0.esw.psc1);}static SYSDEV_ATTR(state_code, 0400, etr_mode_code_show, NULL);static ssize_t etr_untuned_show(struct sys_device *dev, char *buf){ struct etr_aib *aib = etr_aib_from_dev(dev); if (!aib || !aib->slsw.v1) return -ENODATA; return sprintf(buf, "%i\n", aib->edf1.u);}static SYSDEV_ATTR(untuned, 0400, etr_untuned_show, NULL);static ssize_t etr_network_id_show(struct sys_device *dev, char *buf){ struct etr_aib *aib = etr_aib_from_dev(dev); if (!aib || !aib->slsw.v1) return -ENODATA; return sprintf(buf, "%i\n", aib->edf1.net_id);}static SYSDEV_ATTR(network, 0400, etr_network_id_show, NULL);static ssize_t etr_id_show(struct sys_device *dev, char *buf){ struct etr_aib *aib = etr_aib_from_dev(dev); if (!aib || !aib->slsw.v1) return -ENODATA; return sprintf(buf, "%i\n", aib->edf1.etr_id);}static SYSDEV_ATTR(id, 0400, etr_id_show, NULL);static ssize_t etr_port_number_show(struct sys_device *dev, char *buf){ struct etr_aib *aib = etr_aib_from_dev(dev); if (!aib || !aib->slsw.v1) return -ENODATA; return sprintf(buf, "%i\n", aib->edf1.etr_pn);}static SYSDEV_ATTR(port, 0400, etr_port_number_show, NULL);static ssize_t etr_coupled_show(struct sys_device *dev, char *buf){ struct etr_aib *aib = etr_aib_from_dev(dev); if (!aib || !aib->slsw.v3) return -ENODATA; return sprintf(buf, "%i\n", aib->edf3.c);}static SYSDEV_ATTR(coupled, 0400, etr_coupled_show, NULL);static ssize_t etr_local_time_show(struct sys_device *dev, char *buf){ struct etr_aib *aib = etr_aib_from_dev(dev); if (!aib || !aib->slsw.v3) return -ENODATA; return sprintf(buf, "%i\n", aib->edf3.blto);}static SYSDEV_ATTR(local_time, 0400, etr_local_time_show, NULL);static ssize_t etr_utc_offset_show(struct sys_device *dev, char *buf){ struct etr_aib *aib = etr_aib_from_dev(dev); if (!aib || !aib->slsw.v3) return -ENODATA; return sprintf(buf, "%i\n", aib->edf3.buo);}static SYSDEV_ATTR(utc_offset, 0400, etr_utc_offset_show, NULL);static struct sysdev_attribute *etr_port_attributes[] = { &attr_online, &attr_stepping_control, &attr_state_code, &attr_untuned, &attr_network, &attr_id, &attr_port, &attr_coupled, &attr_local_time, &attr_utc_offset, NULL};static int __init etr_register_port(struct sys_device *dev){ struct sysdev_attribute **attr; int rc; rc = sysdev_register(dev); if (rc) goto out; for (attr = etr_port_attributes; *attr; attr++) { rc = sysdev_create_file(dev, *attr); if (rc) goto out_unreg; } return 0;out_unreg: for (; attr >= etr_port_attributes; attr--) sysdev_remove_file(dev, *attr); sysdev_unregister(dev);out: return rc;}static void __init etr_unregister_port(struct sys_device *dev){ struct sysdev_attribute **attr; for (attr = etr_port_attributes; *attr; attr++) sysdev_remove_file(dev, *attr); sysdev_unregister(dev);}static int __init etr_init_sysfs(void){ int rc; rc = sysdev_class_register(&etr_sysclass); if (rc) goto out; rc = sysdev_class_create_file(&etr_sysclass, &attr_stepping_port); if (rc) goto out_unreg_class; rc = sysdev_class_create_file(&etr_sysclass, &attr_stepping_mode); if (rc) goto out_remove_stepping_port; rc = etr_register_port(&etr_port0_dev); if (rc) goto out_remove_stepping_mode; rc = etr_register_port(&etr_port1_dev); if (rc) goto out_remove_port0; return 0;out_remove_port0: etr_unregister_port(&etr_port0_dev);out_remove_stepping_mode: sysdev_class_remove_file(&etr_sysclass, &attr_stepping_mode);out_remove_stepping_port: sysdev_class_remove_file(&etr_sysclass, &attr_stepping_port);out_unreg_class: sysdev_class_unregister(&etr_sysclass);out: return rc;}device_initcall(etr_init_sysfs);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -