clock: Introduce a function to switch the PTP Hardware Clock.

When switching clock devices in JBOD mode, we need to be able to reset the
servo. The existing servo_reset() function will not serve us well, because
in this case we also need to seed the existing frequency offset and limit.
This patch adds a new method that simply starts the servo from scratch.

In the unlikely event of a resource allocation failure, the method will
simply continue to use the previous device, which is better than nothing
and certainly preferable to bailing out the program.

Signed-off-by: Richard Cochran <richardcochran@gmail.com>
master
Richard Cochran 2014-12-17 15:24:02 +01:00
parent d70d38ade3
commit a96797a7fc
2 changed files with 45 additions and 0 deletions

37
clock.c
View File

@ -74,6 +74,7 @@ struct clock_subscriber {
struct clock { struct clock {
clockid_t clkid; clockid_t clkid;
struct servo *servo; struct servo *servo;
enum servo_type servo_type;
struct defaultDS dds; struct defaultDS dds;
struct dataset default_dataset; struct dataset default_dataset;
struct currentDS cur; struct currentDS cur;
@ -862,6 +863,7 @@ struct clock *clock_create(int phc_index, struct interfaces_head *ifaces,
return NULL; return NULL;
} }
c->servo_state = SERVO_UNLOCKED; c->servo_state = SERVO_UNLOCKED;
c->servo_type = servo;
c->delay_filter = filter_create(dds->delay_filter, c->delay_filter = filter_create(dds->delay_filter,
dds->delay_filter_length); dds->delay_filter_length);
if (!c->delay_filter) { if (!c->delay_filter) {
@ -1358,6 +1360,41 @@ UInteger16 clock_steps_removed(struct clock *c)
return c->cur.stepsRemoved; return c->cur.stepsRemoved;
} }
int clock_switch_phc(struct clock *c, int phc_index)
{
struct servo *servo;
int fadj, max_adj;
clockid_t clkid;
char phc[32];
snprintf(phc, 31, "/dev/ptp%d", phc_index);
clkid = phc_open(phc);
if (clkid == CLOCK_INVALID) {
pr_err("Switching PHC, failed to open %s: %m", phc);
return -1;
}
max_adj = phc_max_adj(clkid);
if (!max_adj) {
pr_err("Switching PHC, clock is not adjustable");
phc_close(clkid);
return -1;
}
fadj = (int) clockadj_get_freq(clkid);
clockadj_set_freq(clkid, fadj);
servo = servo_create(c->servo_type, -fadj, max_adj, 0);
if (!servo) {
pr_err("Switching PHC, failed to create clock servo");
phc_close(clkid);
return -1;
}
phc_close(c->clkid);
servo_destroy(c->servo);
c->clkid = clkid;
c->servo = servo;
c->servo_state = SERVO_UNLOCKED;
return 0;
}
enum servo_state clock_synchronize(struct clock *c, enum servo_state clock_synchronize(struct clock *c,
struct timespec ingress_ts, struct timespec ingress_ts,
struct timestamp origin_ts, struct timestamp origin_ts,

View File

@ -204,6 +204,14 @@ int clock_slave_only(struct clock *c);
*/ */
UInteger16 clock_steps_removed(struct clock *c); UInteger16 clock_steps_removed(struct clock *c);
/**
* Switch to a new PTP Hardware Clock, for use with the "jbod" mode.
* @param c The clock instance.
* @param phc_index The index of the PHC device to use.
* @return Zero on success, non-zero otherwise.
*/
int clock_switch_phc(struct clock *c, int phc_index);
/** /**
* Provide a data point to synchronize the clock. * Provide a data point to synchronize the clock.
* @param c The clock instance to synchronize. * @param c The clock instance to synchronize.