Commit 30c72916 authored by Richard Cochran's avatar Richard Cochran Committed by Jeff Kirsher

igb: implement high frequency periodic output signals

In addition to interrupt driven target time output events, the i210
also has two programmable clock outputs.  These clocks support periods
between 16 nanoseconds and 140 milliseconds.  This patch implements
the periodic output function using the clock outputs when possible,
falling back to the target time for longer periods.
Signed-off-by: default avatarRichard Cochran <richardcochran@gmail.com>
Tested-by: default avatarAaron Brown <aaron.f.brown@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 6423fc34
...@@ -104,6 +104,8 @@ ...@@ -104,6 +104,8 @@
#define E1000_TRGTTIMH0 0x0B648 /* Target Time Register 0 High - RW */ #define E1000_TRGTTIMH0 0x0B648 /* Target Time Register 0 High - RW */
#define E1000_TRGTTIML1 0x0B64C /* Target Time Register 1 Low - RW */ #define E1000_TRGTTIML1 0x0B64C /* Target Time Register 1 Low - RW */
#define E1000_TRGTTIMH1 0x0B650 /* Target Time Register 1 High - RW */ #define E1000_TRGTTIMH1 0x0B650 /* Target Time Register 1 High - RW */
#define E1000_FREQOUT0 0x0B654 /* Frequency Out 0 Control Register - RW */
#define E1000_FREQOUT1 0x0B658 /* Frequency Out 1 Control Register - RW */
#define E1000_AUXSTMPL0 0x0B65C /* Auxiliary Time Stamp 0 Register Low - RO */ #define E1000_AUXSTMPL0 0x0B65C /* Auxiliary Time Stamp 0 Register Low - RO */
#define E1000_AUXSTMPH0 0x0B660 /* Auxiliary Time Stamp 0 Register High - RO */ #define E1000_AUXSTMPH0 0x0B660 /* Auxiliary Time Stamp 0 Register High - RO */
#define E1000_AUXSTMPL1 0x0B664 /* Auxiliary Time Stamp 1 Register Low - RO */ #define E1000_AUXSTMPL1 0x0B664 /* Auxiliary Time Stamp 1 Register Low - RO */
......
...@@ -405,7 +405,7 @@ static void igb_pin_extts(struct igb_adapter *igb, int chan, int pin) ...@@ -405,7 +405,7 @@ static void igb_pin_extts(struct igb_adapter *igb, int chan, int pin)
wr32(E1000_CTRL_EXT, ctrl_ext); wr32(E1000_CTRL_EXT, ctrl_ext);
} }
static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin) static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin, int freq)
{ {
static const u32 aux0_sel_sdp[IGB_N_SDP] = { static const u32 aux0_sel_sdp[IGB_N_SDP] = {
AUX0_SEL_SDP0, AUX0_SEL_SDP1, AUX0_SEL_SDP2, AUX0_SEL_SDP3, AUX0_SEL_SDP0, AUX0_SEL_SDP1, AUX0_SEL_SDP2, AUX0_SEL_SDP3,
...@@ -424,6 +424,14 @@ static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin) ...@@ -424,6 +424,14 @@ static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin)
TS_SDP0_SEL_TT1, TS_SDP1_SEL_TT1, TS_SDP0_SEL_TT1, TS_SDP1_SEL_TT1,
TS_SDP2_SEL_TT1, TS_SDP3_SEL_TT1, TS_SDP2_SEL_TT1, TS_SDP3_SEL_TT1,
}; };
static const u32 ts_sdp_sel_fc0[IGB_N_SDP] = {
TS_SDP0_SEL_FC0, TS_SDP1_SEL_FC0,
TS_SDP2_SEL_FC0, TS_SDP3_SEL_FC0,
};
static const u32 ts_sdp_sel_fc1[IGB_N_SDP] = {
TS_SDP0_SEL_FC1, TS_SDP1_SEL_FC1,
TS_SDP2_SEL_FC1, TS_SDP3_SEL_FC1,
};
static const u32 ts_sdp_sel_clr[IGB_N_SDP] = { static const u32 ts_sdp_sel_clr[IGB_N_SDP] = {
TS_SDP0_SEL_FC1, TS_SDP1_SEL_FC1, TS_SDP0_SEL_FC1, TS_SDP1_SEL_FC1,
TS_SDP2_SEL_FC1, TS_SDP3_SEL_FC1, TS_SDP2_SEL_FC1, TS_SDP3_SEL_FC1,
...@@ -445,11 +453,17 @@ static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin) ...@@ -445,11 +453,17 @@ static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin)
tssdp &= ~AUX1_TS_SDP_EN; tssdp &= ~AUX1_TS_SDP_EN;
tssdp &= ~ts_sdp_sel_clr[pin]; tssdp &= ~ts_sdp_sel_clr[pin];
if (freq) {
if (chan == 1)
tssdp |= ts_sdp_sel_fc1[pin];
else
tssdp |= ts_sdp_sel_fc0[pin];
} else {
if (chan == 1) if (chan == 1)
tssdp |= ts_sdp_sel_tt1[pin]; tssdp |= ts_sdp_sel_tt1[pin];
else else
tssdp |= ts_sdp_sel_tt0[pin]; tssdp |= ts_sdp_sel_tt0[pin];
}
tssdp |= ts_sdp_en[pin]; tssdp |= ts_sdp_en[pin];
wr32(E1000_TSSDP, tssdp); wr32(E1000_TSSDP, tssdp);
...@@ -463,10 +477,10 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, ...@@ -463,10 +477,10 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,
struct igb_adapter *igb = struct igb_adapter *igb =
container_of(ptp, struct igb_adapter, ptp_caps); container_of(ptp, struct igb_adapter, ptp_caps);
struct e1000_hw *hw = &igb->hw; struct e1000_hw *hw = &igb->hw;
u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh; u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh, freqout;
unsigned long flags; unsigned long flags;
struct timespec ts; struct timespec ts;
int pin = -1; int use_freq = 0, pin = -1;
s64 ns; s64 ns;
switch (rq->type) { switch (rq->type) {
...@@ -511,40 +525,58 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, ...@@ -511,40 +525,58 @@ static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp,
ts.tv_nsec = rq->perout.period.nsec; ts.tv_nsec = rq->perout.period.nsec;
ns = timespec_to_ns(&ts); ns = timespec_to_ns(&ts);
ns = ns >> 1; ns = ns >> 1;
if (on && ns < 500000LL) { if (on && ns <= 70000000LL) {
/* 2k interrupts per second is an awful lot. */ if (ns < 8LL)
return -EINVAL; return -EINVAL;
use_freq = 1;
} }
ts = ns_to_timespec(ns); ts = ns_to_timespec(ns);
if (rq->perout.index == 1) { if (rq->perout.index == 1) {
if (use_freq) {
tsauxc_mask = TSAUXC_EN_CLK1 | TSAUXC_ST1;
tsim_mask = 0;
} else {
tsauxc_mask = TSAUXC_EN_TT1; tsauxc_mask = TSAUXC_EN_TT1;
tsim_mask = TSINTR_TT1; tsim_mask = TSINTR_TT1;
}
trgttiml = E1000_TRGTTIML1; trgttiml = E1000_TRGTTIML1;
trgttimh = E1000_TRGTTIMH1; trgttimh = E1000_TRGTTIMH1;
freqout = E1000_FREQOUT1;
} else {
if (use_freq) {
tsauxc_mask = TSAUXC_EN_CLK0 | TSAUXC_ST0;
tsim_mask = 0;
} else { } else {
tsauxc_mask = TSAUXC_EN_TT0; tsauxc_mask = TSAUXC_EN_TT0;
tsim_mask = TSINTR_TT0; tsim_mask = TSINTR_TT0;
}
trgttiml = E1000_TRGTTIML0; trgttiml = E1000_TRGTTIML0;
trgttimh = E1000_TRGTTIMH0; trgttimh = E1000_TRGTTIMH0;
freqout = E1000_FREQOUT0;
} }
spin_lock_irqsave(&igb->tmreg_lock, flags); spin_lock_irqsave(&igb->tmreg_lock, flags);
tsauxc = rd32(E1000_TSAUXC); tsauxc = rd32(E1000_TSAUXC);
tsim = rd32(E1000_TSIM); tsim = rd32(E1000_TSIM);
if (rq->perout.index == 1) {
tsauxc &= ~(TSAUXC_EN_TT1 | TSAUXC_EN_CLK1 | TSAUXC_ST1);
tsim &= ~TSINTR_TT1;
} else {
tsauxc &= ~(TSAUXC_EN_TT0 | TSAUXC_EN_CLK0 | TSAUXC_ST0);
tsim &= ~TSINTR_TT0;
}
if (on) { if (on) {
int i = rq->perout.index; int i = rq->perout.index;
igb_pin_perout(igb, i, pin, use_freq);
igb_pin_perout(igb, i, pin);
igb->perout[i].start.tv_sec = rq->perout.start.sec; igb->perout[i].start.tv_sec = rq->perout.start.sec;
igb->perout[i].start.tv_nsec = rq->perout.start.nsec; igb->perout[i].start.tv_nsec = rq->perout.start.nsec;
igb->perout[i].period.tv_sec = ts.tv_sec; igb->perout[i].period.tv_sec = ts.tv_sec;
igb->perout[i].period.tv_nsec = ts.tv_nsec; igb->perout[i].period.tv_nsec = ts.tv_nsec;
wr32(trgttimh, rq->perout.start.sec); wr32(trgttimh, rq->perout.start.sec);
wr32(trgttiml, rq->perout.start.nsec); wr32(trgttiml, rq->perout.start.nsec);
if (use_freq)
wr32(freqout, ns);
tsauxc |= tsauxc_mask; tsauxc |= tsauxc_mask;
tsim |= tsim_mask; tsim |= tsim_mask;
} else {
tsauxc &= ~tsauxc_mask;
tsim &= ~tsim_mask;
} }
wr32(E1000_TSAUXC, tsauxc); wr32(E1000_TSAUXC, tsauxc);
wr32(E1000_TSIM, tsim); wr32(E1000_TSIM, tsim);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment