diff --git a/drivers/net/phy/dp83640.c b/drivers/net/phy/dp83640.c index cb6e0b486b1e220864ba72c60decf9f53b0a05c2..f99937905bda8f81e58f9abd470be37fac6e7f6e 100644 --- a/drivers/net/phy/dp83640.c +++ b/drivers/net/phy/dp83640.c @@ -35,16 +35,15 @@ #define LAYER4 0x02 #define LAYER2 0x01 #define MAX_RXTS 64 -#define N_EXT_TS 1 +#define N_EXT_TS 6 #define PSF_PTPVER 2 #define PSF_EVNT 0x4000 #define PSF_RX 0x2000 #define PSF_TX 0x1000 #define EXT_EVENT 1 -#define EXT_GPIO 1 -#define CAL_EVENT 2 -#define CAL_GPIO 9 -#define CAL_TRIGGER 2 +#define CAL_EVENT 7 +#define CAL_TRIGGER 7 +#define PER_TRIGGER 6 /* phyter seems to miss the mark by 16 ns */ #define ADJTIME_FIX 16 @@ -131,16 +130,30 @@ struct dp83640_clock { /* 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 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(cal_gpio, ushort, 0444); +module_param_array(gpio_tab, ushort, NULL, 0444); MODULE_PARM_DESC(chosen_phy, \ "The address of the PHY to use for the ancillary clock features"); -MODULE_PARM_DESC(cal_gpio, \ - "Which GPIO line to use for synchronizing multiple PHYs"); +MODULE_PARM_DESC(gpio_tab, \ + "Which GPIO line to use for which purpose: cal,perout,extts1,...,extts6"); /* a list of clocks and a mutex to protect it */ static LIST_HEAD(phyter_clocks); @@ -235,6 +248,61 @@ static u64 phy2txts(struct phy_txts *p) 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 */ 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, struct dp83640_clock *clock = container_of(ptp, struct dp83640_clock, caps); struct phy_device *phydev = clock->chosen->phydev; - u16 evnt; + int index; + u16 evnt, event_num, gpio_num; switch (rq->type) { case PTP_CLK_REQ_EXTTS: - if (rq->extts.index != 0) + index = rq->extts.index; + if (index < 0 || index >= N_EXT_TS) 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) { - 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; } ext_write(0, phydev, PAGE5, PTP_EVNT, evnt); return 0; + + case PTP_CLK_REQ_PEROUT: + if (rq->perout.index != 0) + return -EINVAL; + periodic_output(clock, rq, on); + return 0; + default: break; } @@ -441,9 +520,10 @@ static void recalibrate(struct dp83640_clock *clock) struct list_head *this; struct dp83640_private *tmp; 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; + cal_gpio = gpio_tab[CALIBRATE_GPIO]; mutex_lock(&clock->extreg_lock); @@ -542,11 +622,17 @@ static void recalibrate(struct dp83640_clock *clock) /* 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, void *data, u16 ests) { struct phy_txts *phy_txts; struct ptp_clock_event event; + int i, parsed; int words = (ests >> EVNT_TS_LEN_SHIFT) & EVNT_TS_LEN_MASK; u16 ext_status = 0; @@ -568,14 +654,25 @@ static int decode_evnt(struct dp83640_private *dp83640, 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.index = 0; event.timestamp = phy2txts(&dp83640->edata); - ptp_clock_event(dp83640->clock->ptp_clock, &event); + 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); + } + } - words = ext_status ? words + 2 : words + 1; - return words * sizeof(u16); + return parsed * sizeof(u16); } 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) clock->caps.max_adj = 1953124; clock->caps.n_alarm = 0; 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.adjfreq = ptp_dp83640_adjfreq; clock->caps.adjtime = ptp_dp83640_adjtime;