Commit 49b3fd4a authored by Richard Cochran's avatar Richard Cochran Committed by David S. Miller

dp83640: enable six external events and one periodic output

This patch enables six external event channels and one periodic output.
One GPIO is reserved for synchronizing multiple PHYs. The assignment
of GPIO functions can be changed via a module parameter.

The code supports multiple simultaneous events by inducing a PTP clock
event for every channel marked in the PHY's extended status word.
Signed-off-by: default avatarRichard Cochran <richard.cochran@omicron.at>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7777de9a
...@@ -35,16 +35,15 @@ ...@@ -35,16 +35,15 @@
#define LAYER4 0x02 #define LAYER4 0x02
#define LAYER2 0x01 #define LAYER2 0x01
#define MAX_RXTS 64 #define MAX_RXTS 64
#define N_EXT_TS 1 #define N_EXT_TS 6
#define PSF_PTPVER 2 #define PSF_PTPVER 2
#define PSF_EVNT 0x4000 #define PSF_EVNT 0x4000
#define PSF_RX 0x2000 #define PSF_RX 0x2000
#define PSF_TX 0x1000 #define PSF_TX 0x1000
#define EXT_EVENT 1 #define EXT_EVENT 1
#define EXT_GPIO 1 #define CAL_EVENT 7
#define CAL_EVENT 2 #define CAL_TRIGGER 7
#define CAL_GPIO 9 #define PER_TRIGGER 6
#define CAL_TRIGGER 2
/* phyter seems to miss the mark by 16 ns */ /* phyter seems to miss the mark by 16 ns */
#define ADJTIME_FIX 16 #define ADJTIME_FIX 16
...@@ -131,16 +130,30 @@ struct dp83640_clock { ...@@ -131,16 +130,30 @@ struct dp83640_clock {
/* globals */ /* globals */
enum {
CALIBRATE_GPIO,
PEROUT_GPIO,
EXTTS0_GPIO,
EXTTS1_GPIO,
EXTTS2_GPIO,
EXTTS3_GPIO,
EXTTS4_GPIO,
EXTTS5_GPIO,
GPIO_TABLE_SIZE
};
static int chosen_phy = -1; static int chosen_phy = -1;
static ushort cal_gpio = 4; static ushort gpio_tab[GPIO_TABLE_SIZE] = {
1, 2, 3, 4, 8, 9, 10, 11
};
module_param(chosen_phy, int, 0444); module_param(chosen_phy, int, 0444);
module_param(cal_gpio, ushort, 0444); module_param_array(gpio_tab, ushort, NULL, 0444);
MODULE_PARM_DESC(chosen_phy, \ MODULE_PARM_DESC(chosen_phy, \
"The address of the PHY to use for the ancillary clock features"); "The address of the PHY to use for the ancillary clock features");
MODULE_PARM_DESC(cal_gpio, \ MODULE_PARM_DESC(gpio_tab, \
"Which GPIO line to use for synchronizing multiple PHYs"); "Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6");
/* a list of clocks and a mutex to protect it */ /* a list of clocks and a mutex to protect it */
static LIST_HEAD(phyter_clocks); static LIST_HEAD(phyter_clocks);
...@@ -235,6 +248,61 @@ static u64 phy2txts(struct phy_txts *p) ...@@ -235,6 +248,61 @@ static u64 phy2txts(struct phy_txts *p)
return ns; return ns;
} }
static void periodic_output(struct dp83640_clock *clock,
struct ptp_clock_request *clkreq, bool on)
{
struct dp83640_private *dp83640 = clock->chosen;
struct phy_device *phydev = dp83640->phydev;
u32 sec, nsec, period;
u16 gpio, ptp_trig, trigger, val;
gpio = on ? gpio_tab[PEROUT_GPIO] : 0;
trigger = PER_TRIGGER;
ptp_trig = TRIG_WR |
(trigger & TRIG_CSEL_MASK) << TRIG_CSEL_SHIFT |
(gpio & TRIG_GPIO_MASK) << TRIG_GPIO_SHIFT |
TRIG_PER |
TRIG_PULSE;
val = (trigger & TRIG_SEL_MASK) << TRIG_SEL_SHIFT;
if (!on) {
val |= TRIG_DIS;
mutex_lock(&clock->extreg_lock);
ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig);
ext_write(0, phydev, PAGE4, PTP_CTL, val);
mutex_unlock(&clock->extreg_lock);
return;
}
sec = clkreq->perout.start.sec;
nsec = clkreq->perout.start.nsec;
period = clkreq->perout.period.sec * 1000000000UL;
period += clkreq->perout.period.nsec;
mutex_lock(&clock->extreg_lock);
ext_write(0, phydev, PAGE5, PTP_TRIG, ptp_trig);
/*load trigger*/
val |= TRIG_LOAD;
ext_write(0, phydev, PAGE4, PTP_CTL, val);
ext_write(0, phydev, PAGE4, PTP_TDR, nsec & 0xffff); /* ns[15:0] */
ext_write(0, phydev, PAGE4, PTP_TDR, nsec >> 16); /* ns[31:16] */
ext_write(0, phydev, PAGE4, PTP_TDR, sec & 0xffff); /* sec[15:0] */
ext_write(0, phydev, PAGE4, PTP_TDR, sec >> 16); /* sec[31:16] */
ext_write(0, phydev, PAGE4, PTP_TDR, period & 0xffff); /* ns[15:0] */
ext_write(0, phydev, PAGE4, PTP_TDR, period >> 16); /* ns[31:16] */
/*enable trigger*/
val &= ~TRIG_LOAD;
val |= TRIG_EN;
ext_write(0, phydev, PAGE4, PTP_CTL, val);
mutex_unlock(&clock->extreg_lock);
}
/* ptp clock methods */ /* ptp clock methods */
static int ptp_dp83640_adjfreq(struct ptp_clock_info *ptp, s32 ppb) static int ptp_dp83640_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
...@@ -338,19 +406,30 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp, ...@@ -338,19 +406,30 @@ static int ptp_dp83640_enable(struct ptp_clock_info *ptp,
struct dp83640_clock *clock = struct dp83640_clock *clock =
container_of(ptp, struct dp83640_clock, caps); container_of(ptp, struct dp83640_clock, caps);
struct phy_device *phydev = clock->chosen->phydev; struct phy_device *phydev = clock->chosen->phydev;
u16 evnt; int index;
u16 evnt, event_num, gpio_num;
switch (rq->type) { switch (rq->type) {
case PTP_CLK_REQ_EXTTS: case PTP_CLK_REQ_EXTTS:
if (rq->extts.index != 0) index = rq->extts.index;
if (index < 0 || index >= N_EXT_TS)
return -EINVAL; return -EINVAL;
evnt = EVNT_WR | (EXT_EVENT & EVNT_SEL_MASK) << EVNT_SEL_SHIFT; event_num = EXT_EVENT + index;
evnt = EVNT_WR | (event_num & EVNT_SEL_MASK) << EVNT_SEL_SHIFT;
if (on) { if (on) {
evnt |= (EXT_GPIO & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT; gpio_num = gpio_tab[EXTTS0_GPIO + index];
evnt |= (gpio_num & EVNT_GPIO_MASK) << EVNT_GPIO_SHIFT;
evnt |= EVNT_RISE; evnt |= EVNT_RISE;
} }
ext_write(0, phydev, PAGE5, PTP_EVNT, evnt); ext_write(0, phydev, PAGE5, PTP_EVNT, evnt);
return 0; return 0;
case PTP_CLK_REQ_PEROUT:
if (rq->perout.index != 0)
return -EINVAL;
periodic_output(clock, rq, on);
return 0;
default: default:
break; break;
} }
...@@ -441,9 +520,10 @@ static void recalibrate(struct dp83640_clock *clock) ...@@ -441,9 +520,10 @@ static void recalibrate(struct dp83640_clock *clock)
struct list_head *this; struct list_head *this;
struct dp83640_private *tmp; struct dp83640_private *tmp;
struct phy_device *master = clock->chosen->phydev; struct phy_device *master = clock->chosen->phydev;
u16 cfg0, evnt, ptp_trig, trigger, val; u16 cal_gpio, cfg0, evnt, ptp_trig, trigger, val;
trigger = CAL_TRIGGER; trigger = CAL_TRIGGER;
cal_gpio = gpio_tab[CALIBRATE_GPIO];
mutex_lock(&clock->extreg_lock); mutex_lock(&clock->extreg_lock);
...@@ -542,11 +622,17 @@ static void recalibrate(struct dp83640_clock *clock) ...@@ -542,11 +622,17 @@ static void recalibrate(struct dp83640_clock *clock)
/* time stamping methods */ /* time stamping methods */
static inline u16 exts_chan_to_edata(int ch)
{
return 1 << ((ch + EXT_EVENT) * 2);
}
static int decode_evnt(struct dp83640_private *dp83640, static int decode_evnt(struct dp83640_private *dp83640,
void *data, u16 ests) void *data, u16 ests)
{ {
struct phy_txts *phy_txts; struct phy_txts *phy_txts;
struct ptp_clock_event event; struct ptp_clock_event event;
int i, parsed;
int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK; int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK;
u16 ext_status = 0; u16 ext_status = 0;
...@@ -568,14 +654,25 @@ static int decode_evnt(struct dp83640_private *dp83640, ...@@ -568,14 +654,25 @@ static int decode_evnt(struct dp83640_private *dp83640,
dp83640->edata.ns_lo = phy_txts->ns_lo; dp83640->edata.ns_lo = phy_txts->ns_lo;
} }
if (ext_status) {
parsed = words + 2;
} else {
parsed = words + 1;
i = ((ests >> EVNT_NUM_SHIFT) & EVNT_NUM_MASK) - EXT_EVENT;
ext_status = exts_chan_to_edata(i);
}
event.type = PTP_CLOCK_EXTTS; event.type = PTP_CLOCK_EXTTS;
event.index = 0;
event.timestamp = phy2txts(&dp83640->edata); event.timestamp = phy2txts(&dp83640->edata);
for (i = 0; i < N_EXT_TS; i++) {
if (ext_status & exts_chan_to_edata(i)) {
event.index = i;
ptp_clock_event(dp83640->clock->ptp_clock, &event); ptp_clock_event(dp83640->clock->ptp_clock, &event);
}
}
words = ext_status ? words + 2 : words + 1; return parsed * sizeof(u16);
return words * sizeof(u16);
} }
static void decode_rxts(struct dp83640_private *dp83640, static void decode_rxts(struct dp83640_private *dp83640,
...@@ -740,7 +837,7 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus) ...@@ -740,7 +837,7 @@ static void dp83640_clock_init(struct dp83640_clock *clock, struct mii_bus *bus)
clock->caps.max_adj = 1953124; clock->caps.max_adj = 1953124;
clock->caps.n_alarm = 0; clock->caps.n_alarm = 0;
clock->caps.n_ext_ts = N_EXT_TS; clock->caps.n_ext_ts = N_EXT_TS;
clock->caps.n_per_out = 0; clock->caps.n_per_out = 1;
clock->caps.pps = 0; clock->caps.pps = 0;
clock->caps.adjfreq = ptp_dp83640_adjfreq; clock->caps.adjfreq = ptp_dp83640_adjfreq;
clock->caps.adjtime = ptp_dp83640_adjtime; clock->caps.adjtime = ptp_dp83640_adjtime;
......
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