Skip to content

Commit 4eb3be2

Browse files
bstreiffdavem330
authored andcommitted
net: dsa: mv88e6xxx: add support for event capture
This patch adds support for configuring mv88e6xxx GPIO lines as PTP pins, so that they may be used for time stamping external events or for periodic output. Checkpatch and reverse Christmas tree fixes by Andrew Lunn Periodic output removed by Richard Cochran, until a better abstraction of a VCO is added to Linux in general. Signed-off-by: Brandon Streiff <[email protected]> Signed-off-by: Andrew Lunn <[email protected]> Signed-off-by: Richard Cochran <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent a73ccd6 commit 4eb3be2

File tree

3 files changed

+207
-2
lines changed

3 files changed

+207
-2
lines changed

drivers/net/dsa/mv88e6xxx/chip.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,10 @@ struct mv88e6xxx_chip {
232232

233233
struct ptp_clock *ptp_clock;
234234
struct ptp_clock_info ptp_clock_info;
235+
struct delayed_work tai_event_work;
236+
struct ptp_pin_desc pin_config[MV88E6XXX_MAX_GPIO];
237+
u16 trig_config;
238+
u16 evcap_config;
235239
};
236240

237241
struct mv88e6xxx_bus_ops {

drivers/net/dsa/mv88e6xxx/ptp.c

Lines changed: 187 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
ptp_clock_info)
3232
#define dw_overflow_to_chip(dw) container_of(dw, struct mv88e6xxx_chip, \
3333
overflow_work)
34+
#define dw_tai_event_to_chip(dw) container_of(dw, struct mv88e6xxx_chip, \
35+
tai_event_work)
3436

3537
static int mv88e6xxx_tai_read(struct mv88e6xxx_chip *chip, int addr,
3638
u16 *data, int len)
@@ -41,6 +43,30 @@ static int mv88e6xxx_tai_read(struct mv88e6xxx_chip *chip, int addr,
4143
return chip->info->ops->avb_ops->tai_read(chip, addr, data, len);
4244
}
4345

46+
static int mv88e6xxx_tai_write(struct mv88e6xxx_chip *chip, int addr, u16 data)
47+
{
48+
if (!chip->info->ops->avb_ops->tai_write)
49+
return -EOPNOTSUPP;
50+
51+
return chip->info->ops->avb_ops->tai_write(chip, addr, data);
52+
}
53+
54+
/* TODO: places where this are called should be using pinctrl */
55+
static int mv88e6xxx_set_gpio_func(struct mv88e6xxx_chip *chip, int pin,
56+
int func, int input)
57+
{
58+
int err;
59+
60+
if (!chip->info->ops->gpio_ops)
61+
return -EOPNOTSUPP;
62+
63+
err = chip->info->ops->gpio_ops->set_dir(chip, pin, input);
64+
if (err)
65+
return err;
66+
67+
return chip->info->ops->gpio_ops->set_pctl(chip, pin, func);
68+
}
69+
4470
static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc)
4571
{
4672
struct mv88e6xxx_chip *chip = cc_to_chip(cc);
@@ -55,6 +81,92 @@ static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc)
5581
return ((u32)phc_time[1] << 16) | phc_time[0];
5682
}
5783

84+
/* mv88e6xxx_config_eventcap - configure TAI event capture
85+
* @event: PTP_CLOCK_PPS (internal) or PTP_CLOCK_EXTTS (external)
86+
* @rising: zero for falling-edge trigger, else rising-edge trigger
87+
*
88+
* This will also reset the capture sequence counter.
89+
*/
90+
static int mv88e6xxx_config_eventcap(struct mv88e6xxx_chip *chip, int event,
91+
int rising)
92+
{
93+
u16 global_config;
94+
u16 cap_config;
95+
int err;
96+
97+
chip->evcap_config = MV88E6XXX_TAI_CFG_CAP_OVERWRITE |
98+
MV88E6XXX_TAI_CFG_CAP_CTR_START;
99+
if (!rising)
100+
chip->evcap_config |= MV88E6XXX_TAI_CFG_EVREQ_FALLING;
101+
102+
global_config = (chip->evcap_config | chip->trig_config);
103+
err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_CFG, global_config);
104+
if (err)
105+
return err;
106+
107+
if (event == PTP_CLOCK_PPS) {
108+
cap_config = MV88E6XXX_TAI_EVENT_STATUS_CAP_TRIG;
109+
} else if (event == PTP_CLOCK_EXTTS) {
110+
/* if STATUS_CAP_TRIG is unset we capture PTP_EVREQ events */
111+
cap_config = 0;
112+
} else {
113+
return -EINVAL;
114+
}
115+
116+
/* Write the capture config; this also clears the capture counter */
117+
err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_EVENT_STATUS,
118+
cap_config);
119+
120+
return err;
121+
}
122+
123+
static void mv88e6xxx_tai_event_work(struct work_struct *ugly)
124+
{
125+
struct delayed_work *dw = to_delayed_work(ugly);
126+
struct mv88e6xxx_chip *chip = dw_tai_event_to_chip(dw);
127+
struct ptp_clock_event ev;
128+
u16 status[4];
129+
u32 raw_ts;
130+
int err;
131+
132+
mutex_lock(&chip->reg_lock);
133+
err = mv88e6xxx_tai_read(chip, MV88E6XXX_TAI_EVENT_STATUS,
134+
status, ARRAY_SIZE(status));
135+
mutex_unlock(&chip->reg_lock);
136+
137+
if (err) {
138+
dev_err(chip->dev, "failed to read TAI status register\n");
139+
return;
140+
}
141+
if (status[0] & MV88E6XXX_TAI_EVENT_STATUS_ERROR) {
142+
dev_warn(chip->dev, "missed event capture\n");
143+
return;
144+
}
145+
if (!(status[0] & MV88E6XXX_TAI_EVENT_STATUS_VALID))
146+
goto out;
147+
148+
raw_ts = ((u32)status[2] << 16) | status[1];
149+
150+
/* Clear the valid bit so the next timestamp can come in */
151+
status[0] &= ~MV88E6XXX_TAI_EVENT_STATUS_VALID;
152+
mutex_lock(&chip->reg_lock);
153+
err = mv88e6xxx_tai_write(chip, MV88E6XXX_TAI_EVENT_STATUS, status[0]);
154+
mutex_unlock(&chip->reg_lock);
155+
156+
/* This is an external timestamp */
157+
ev.type = PTP_CLOCK_EXTTS;
158+
159+
/* We only have one timestamping channel. */
160+
ev.index = 0;
161+
mutex_lock(&chip->reg_lock);
162+
ev.timestamp = timecounter_cyc2time(&chip->tstamp_tc, raw_ts);
163+
mutex_unlock(&chip->reg_lock);
164+
165+
ptp_clock_event(chip->ptp_clock, &ev);
166+
out:
167+
schedule_delayed_work(&chip->tai_event_work, TAI_EVENT_WORK_INTERVAL);
168+
}
169+
58170
static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
59171
{
60172
struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
@@ -122,16 +234,71 @@ static int mv88e6xxx_ptp_settime(struct ptp_clock_info *ptp,
122234
return 0;
123235
}
124236

237+
static int mv88e6xxx_ptp_enable_extts(struct mv88e6xxx_chip *chip,
238+
struct ptp_clock_request *rq, int on)
239+
{
240+
int rising = (rq->extts.flags & PTP_RISING_EDGE);
241+
int func;
242+
int pin;
243+
int err;
244+
245+
pin = ptp_find_pin(chip->ptp_clock, PTP_PF_EXTTS, rq->extts.index);
246+
247+
if (pin < 0)
248+
return -EBUSY;
249+
250+
mutex_lock(&chip->reg_lock);
251+
252+
if (on) {
253+
func = MV88E6352_G2_SCRATCH_GPIO_PCTL_EVREQ;
254+
255+
err = mv88e6xxx_set_gpio_func(chip, pin, func, true);
256+
if (err)
257+
goto out;
258+
259+
schedule_delayed_work(&chip->tai_event_work,
260+
TAI_EVENT_WORK_INTERVAL);
261+
262+
err = mv88e6xxx_config_eventcap(chip, PTP_CLOCK_EXTTS, rising);
263+
} else {
264+
func = MV88E6352_G2_SCRATCH_GPIO_PCTL_GPIO;
265+
266+
err = mv88e6xxx_set_gpio_func(chip, pin, func, true);
267+
268+
cancel_delayed_work_sync(&chip->tai_event_work);
269+
}
270+
271+
out:
272+
mutex_unlock(&chip->reg_lock);
273+
274+
return err;
275+
}
276+
125277
static int mv88e6xxx_ptp_enable(struct ptp_clock_info *ptp,
126278
struct ptp_clock_request *rq, int on)
127279
{
128-
return -EOPNOTSUPP;
280+
struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
281+
282+
switch (rq->type) {
283+
case PTP_CLK_REQ_EXTTS:
284+
return mv88e6xxx_ptp_enable_extts(chip, rq, on);
285+
default:
286+
return -EOPNOTSUPP;
287+
}
129288
}
130289

131290
static int mv88e6xxx_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
132291
enum ptp_pin_function func, unsigned int chan)
133292
{
134-
return -EOPNOTSUPP;
293+
switch (func) {
294+
case PTP_PF_NONE:
295+
case PTP_PF_EXTTS:
296+
break;
297+
case PTP_PF_PEROUT:
298+
case PTP_PF_PHYSYNC:
299+
return -EOPNOTSUPP;
300+
}
301+
return 0;
135302
}
136303

137304
/* With a 125MHz input clock, the 32-bit timestamp counter overflows in ~34.3
@@ -152,6 +319,8 @@ static void mv88e6xxx_ptp_overflow_check(struct work_struct *work)
152319

153320
int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
154321
{
322+
int i;
323+
155324
/* Set up the cycle counter */
156325
memset(&chip->tstamp_cc, 0, sizeof(chip->tstamp_cc));
157326
chip->tstamp_cc.read = mv88e6xxx_ptp_clock_read;
@@ -163,12 +332,27 @@ int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
163332
ktime_to_ns(ktime_get_real()));
164333

165334
INIT_DELAYED_WORK(&chip->overflow_work, mv88e6xxx_ptp_overflow_check);
335+
INIT_DELAYED_WORK(&chip->tai_event_work, mv88e6xxx_tai_event_work);
166336

167337
chip->ptp_clock_info.owner = THIS_MODULE;
168338
snprintf(chip->ptp_clock_info.name, sizeof(chip->ptp_clock_info.name),
169339
dev_name(chip->dev));
170340
chip->ptp_clock_info.max_adj = 1000000;
171341

342+
chip->ptp_clock_info.n_ext_ts = 1;
343+
chip->ptp_clock_info.n_per_out = 0;
344+
chip->ptp_clock_info.n_pins = mv88e6xxx_num_gpio(chip);
345+
chip->ptp_clock_info.pps = 0;
346+
347+
for (i = 0; i < chip->ptp_clock_info.n_pins; ++i) {
348+
struct ptp_pin_desc *ppd = &chip->pin_config[i];
349+
350+
snprintf(ppd->name, sizeof(ppd->name), "mv88e6xxx_gpio%d", i);
351+
ppd->index = i;
352+
ppd->func = PTP_PF_NONE;
353+
}
354+
chip->ptp_clock_info.pin_config = chip->pin_config;
355+
172356
chip->ptp_clock_info.adjfine = mv88e6xxx_ptp_adjfine;
173357
chip->ptp_clock_info.adjtime = mv88e6xxx_ptp_adjtime;
174358
chip->ptp_clock_info.gettime64 = mv88e6xxx_ptp_gettime;
@@ -190,6 +374,7 @@ void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
190374
{
191375
if (chip->ptp_clock) {
192376
cancel_delayed_work_sync(&chip->overflow_work);
377+
cancel_delayed_work_sync(&chip->tai_event_work);
193378

194379
ptp_clock_unregister(chip->ptp_clock);
195380
chip->ptp_clock = NULL;

drivers/net/dsa/mv88e6xxx/ptp.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@
2121

2222
/* Offset 0x00: TAI Global Config */
2323
#define MV88E6XXX_TAI_CFG 0x00
24+
#define MV88E6XXX_TAI_CFG_CAP_OVERWRITE 0x8000
25+
#define MV88E6XXX_TAI_CFG_CAP_CTR_START 0x4000
26+
#define MV88E6XXX_TAI_CFG_EVREQ_FALLING 0x2000
27+
#define MV88E6XXX_TAI_CFG_TRIG_ACTIVE_LO 0x1000
28+
#define MV88E6XXX_TAI_CFG_IRL_ENABLE 0x0400
29+
#define MV88E6XXX_TAI_CFG_TRIG_IRQ_EN 0x0200
30+
#define MV88E6XXX_TAI_CFG_EVREQ_IRQ_EN 0x0100
31+
#define MV88E6XXX_TAI_CFG_TRIG_LOCK 0x0080
32+
#define MV88E6XXX_TAI_CFG_BLOCK_UPDATE 0x0008
33+
#define MV88E6XXX_TAI_CFG_MULTI_PTP 0x0004
34+
#define MV88E6XXX_TAI_CFG_TRIG_MODE_ONESHOT 0x0002
35+
#define MV88E6XXX_TAI_CFG_TRIG_ENABLE 0x0001
2436

2537
/* Offset 0x01: Timestamp Clock Period (ps) */
2638
#define MV88E6XXX_TAI_CLOCK_PERIOD 0x01
@@ -46,6 +58,10 @@
4658

4759
/* Offset 0x09: Event Status */
4860
#define MV88E6XXX_TAI_EVENT_STATUS 0x09
61+
#define MV88E6XXX_TAI_EVENT_STATUS_CAP_TRIG 0x4000
62+
#define MV88E6XXX_TAI_EVENT_STATUS_ERROR 0x0200
63+
#define MV88E6XXX_TAI_EVENT_STATUS_VALID 0x0100
64+
#define MV88E6XXX_TAI_EVENT_STATUS_CTR_MASK 0x00ff
4965

5066
/* Offset 0x0A/0x0B: Event Time */
5167
#define MV88E6XXX_TAI_EVENT_TIME_LO 0x0a

0 commit comments

Comments
 (0)