/* Get nanoseconds from PTP clock */ static u64 hellcreek_ptp_clock_read(struct hellcreek *hellcreek, struct ptp_system_timestamp *sts)
{
u16 nsl, nsh;
/* Take a snapshot */
hellcreek_ptp_write(hellcreek, PR_COMMAND_C_SS, PR_COMMAND_C);
/* The time of the day is saved as 96 bits. However, due to hardware * limitations the seconds are not or only partly kept in the PTP * core. Currently only three bits for the seconds are available. That's * why only the nanoseconds are used and the seconds are tracked in * software. Anyway due to internal locking all five registers should be * read.
*/
nsh = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
nsh = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
nsh = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
nsh = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
ptp_read_system_prets(sts);
nsl = hellcreek_ptp_read(hellcreek, PR_SS_SYNC_DATA_C);
ptp_read_system_postts(sts);
/* Retrieve the seconds parts in nanoseconds for a packet timestamped with @ns. * There has to be a check whether an overflow occurred between the packet * arrival and now. If so use the correct seconds (-1) for calculating the * packet arrival time.
*/
u64 hellcreek_ptp_gettime_seconds(struct hellcreek *hellcreek, u64 ns)
{
u64 s;
__hellcreek_ptp_gettime(hellcreek, NULL); if (hellcreek->last_ts > ns)
s = hellcreek->seconds * NSEC_PER_SEC; else
s = (hellcreek->seconds - 1) * NSEC_PER_SEC;
/* IP-Core adjusts the nominal frequency by adding or subtracting 1 ns * from the 8 ns (period of the oscillator) every time the accumulator * register overflows. The value stored in the addend register is added * to the accumulator register every 8 ns. * * addend value = (2^30 * accumulator_overflow_rate) / * oscillator_frequency * where: * * oscillator_frequency = 125 MHz * accumulator_overflow_rate = 125 MHz * scaled_ppm * 2^-16 * 10^-6 * 8
*/
adj = scaled_ppm;
adj <<= 11;
addend = (u32)div_u64(adj, 15625);
/* If the offset is larger than IP-Core slow offset resources. Don't * consider slow adjustment. Rather, add the offset directly to the * current time
*/ if (abs(delta) > MAX_SLOW_OFFSET_ADJ) { struct timespec64 now, then = ns_to_timespec64(delta);
hellcreek_ptp_gettimex(ptp, &now, NULL);
now = timespec64_add(now, then);
hellcreek_ptp_settime(ptp, &now);
return 0;
}
if (delta < 0) {
negative = 1;
delta = -delta;
}
/* 'count_val' does not exceed the maximum register size (2^30) */
count_val = div_s64(delta, MAX_NS_PER_STEP);
/* There two available LEDs internally called sync_good and is_gm. However, the * user might want to use a different label and specify the default state. Take * those properties from device tree.
*/ staticint hellcreek_led_setup(struct hellcreek *hellcreek)
{ struct device_node *leds, *led = NULL; enum led_default_state state; constchar *label; int ret = -EINVAL;
of_node_get(hellcreek->dev->of_node);
leds = of_find_node_by_name(hellcreek->dev->of_node, "leds"); if (!leds) {
dev_err(hellcreek->dev, "No LEDs specified in device tree!\n"); return ret;
}
hellcreek->status_out = 0;
led = of_get_next_available_child(leds, led); if (!led) {
dev_err(hellcreek->dev, "First LED not specified!\n"); goto out;
}
ret = of_property_read_string(led, "label", &label);
hellcreek->led_sync_good.name = ret ? "sync_good" : label;
state = led_init_default_state_get(of_fwnode_handle(led)); switch (state) { case LEDS_DEFSTATE_ON:
hellcreek->led_sync_good.brightness = 1; break; case LEDS_DEFSTATE_KEEP:
hellcreek->led_sync_good.brightness =
hellcreek_get_brightness(hellcreek, STATUS_OUT_SYNC_GOOD); break; default:
hellcreek->led_sync_good.brightness = 0;
}
/* Set initial state */ if (hellcreek->led_sync_good.brightness == 1)
hellcreek_set_brightness(hellcreek, STATUS_OUT_SYNC_GOOD, 1); if (hellcreek->led_is_gm.brightness == 1)
hellcreek_set_brightness(hellcreek, STATUS_OUT_IS_GM, 1);
/* Register both leds */
led_classdev_register(hellcreek->dev, &hellcreek->led_sync_good);
led_classdev_register(hellcreek->dev, &hellcreek->led_is_gm);
ret = 0;
out:
of_node_put(leds);
return ret;
}
int hellcreek_ptp_setup(struct hellcreek *hellcreek)
{
u16 status; int ret;
/* Set up the overflow work */
INIT_DELAYED_WORK(&hellcreek->overflow_work,
hellcreek_ptp_overflow_check);
/* IP-Core can add up to 0.5 ns per 8 ns cycle, which means * accumulator_overflow_rate shall not exceed 62.5 MHz (which adjusts * the nominal frequency by 6.25%)
*/
hellcreek->ptp_clock_info.max_adj = 62500000;
hellcreek->ptp_clock_info.n_alarm = 0;
hellcreek->ptp_clock_info.n_pins = 0;
hellcreek->ptp_clock_info.n_ext_ts = 0;
hellcreek->ptp_clock_info.n_per_out = 0;
hellcreek->ptp_clock_info.pps = 0;
hellcreek->ptp_clock_info.adjfine = hellcreek_ptp_adjfine;
hellcreek->ptp_clock_info.adjtime = hellcreek_ptp_adjtime;
hellcreek->ptp_clock_info.gettimex64 = hellcreek_ptp_gettimex;
hellcreek->ptp_clock_info.settime64 = hellcreek_ptp_settime;
hellcreek->ptp_clock_info.enable = hellcreek_ptp_enable;
hellcreek->ptp_clock_info.do_aux_work = hellcreek_hwtstamp_work;
hellcreek->ptp_clock = ptp_clock_register(&hellcreek->ptp_clock_info,
hellcreek->dev); if (IS_ERR(hellcreek->ptp_clock)) return PTR_ERR(hellcreek->ptp_clock);
/* Enable the offset correction process, if no offset correction is * already taking place
*/
status = hellcreek_ptp_read(hellcreek, PR_CLOCK_STATUS_C); if (!(status & PR_CLOCK_STATUS_C_OFS_ACT))
hellcreek_ptp_write(hellcreek,
status | PR_CLOCK_STATUS_C_ENA_OFS,
PR_CLOCK_STATUS_C);
/* Enable the drift correction process */
hellcreek_ptp_write(hellcreek, status | PR_CLOCK_STATUS_C_ENA_DRIFT,
PR_CLOCK_STATUS_C);
/* LED setup */
ret = hellcreek_led_setup(hellcreek); if (ret) { if (hellcreek->ptp_clock)
ptp_clock_unregister(hellcreek->ptp_clock); return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.