Commit f3d8e0a9 authored by Horatiu Vultur's avatar Horatiu Vultur Committed by David S. Miller

net: lan966x: Add support for PTP_PF_EXTTS

Extend the PTP programmable pins to implement also PTP_PF_EXTTS
function. The PTP pin can be configured to capture only on the rising
edge of the PPS signal. And once an event is seen then an interrupt is
generated and the local time counter is saved.
The interrupt is shared between all the pins.
Signed-off-by: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 2b7ff258
......@@ -692,6 +692,9 @@ static void lan966x_cleanup_ports(struct lan966x *lan966x)
if (lan966x->ptp_irq)
devm_free_irq(lan966x->dev, lan966x->ptp_irq, lan966x);
if (lan966x->ptp_ext_irq)
devm_free_irq(lan966x->dev, lan966x->ptp_ext_irq, lan966x);
}
static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
......@@ -1058,6 +1061,20 @@ static int lan966x_probe(struct platform_device *pdev)
lan966x->fdma = true;
}
if (lan966x->ptp) {
lan966x->ptp_ext_irq = platform_get_irq_byname(pdev, "ptp-ext");
if (lan966x->ptp_ext_irq > 0) {
err = devm_request_threaded_irq(&pdev->dev,
lan966x->ptp_ext_irq, NULL,
lan966x_ptp_ext_irq_handler,
IRQF_ONESHOT,
"ptp-ext irq", lan966x);
if (err)
return dev_err_probe(&pdev->dev, err,
"Unable to use ptp-ext irq");
}
}
/* init switch */
lan966x_init(lan966x);
lan966x_stats_init(lan966x);
......
......@@ -233,6 +233,7 @@ struct lan966x {
int ana_irq;
int ptp_irq;
int fdma_irq;
int ptp_ext_irq;
/* worqueue for fdb */
struct workqueue_struct *fdb_work;
......@@ -394,6 +395,7 @@ int lan966x_ptp_txtstamp_request(struct lan966x_port *port,
void lan966x_ptp_txtstamp_release(struct lan966x_port *port,
struct sk_buff *skb);
irqreturn_t lan966x_ptp_irq_handler(int irq, void *args);
irqreturn_t lan966x_ptp_ext_irq_handler(int irq, void *args);
int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev);
int lan966x_fdma_change_mtu(struct lan966x *lan966x);
......
......@@ -321,6 +321,63 @@ irqreturn_t lan966x_ptp_irq_handler(int irq, void *args)
return IRQ_HANDLED;
}
irqreturn_t lan966x_ptp_ext_irq_handler(int irq, void *args)
{
struct lan966x *lan966x = args;
struct lan966x_phc *phc;
unsigned long flags;
u64 time = 0;
time64_t s;
int pin, i;
s64 ns;
if (!(lan_rd(lan966x, PTP_PIN_INTR)))
return IRQ_NONE;
/* Go through all domains and see which pin generated the interrupt */
for (i = 0; i < LAN966X_PHC_COUNT; ++i) {
struct ptp_clock_event ptp_event = {0};
phc = &lan966x->phc[i];
pin = ptp_find_pin_unlocked(phc->clock, PTP_PF_EXTTS, 0);
if (pin == -1)
continue;
if (!(lan_rd(lan966x, PTP_PIN_INTR) & BIT(pin)))
continue;
spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
/* Enable to get the new interrupt.
* By writing 1 it clears the bit
*/
lan_wr(BIT(pin), lan966x, PTP_PIN_INTR);
/* Get current time */
s = lan_rd(lan966x, PTP_TOD_SEC_MSB(pin));
s <<= 32;
s |= lan_rd(lan966x, PTP_TOD_SEC_LSB(pin));
ns = lan_rd(lan966x, PTP_TOD_NSEC(pin));
ns &= PTP_TOD_NSEC_TOD_NSEC;
spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
if ((ns & 0xFFFFFFF0) == 0x3FFFFFF0) {
s--;
ns &= 0xf;
ns += 999999984;
}
time = ktime_set(s, ns);
ptp_event.index = pin;
ptp_event.timestamp = time;
ptp_event.type = PTP_CLOCK_EXTTS;
ptp_clock_event(phc->clock, &ptp_event);
}
return IRQ_HANDLED;
}
static int lan966x_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
......@@ -508,6 +565,7 @@ static int lan966x_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
switch (func) {
case PTP_PF_NONE:
case PTP_PF_PEROUT:
case PTP_PF_EXTTS:
break;
default:
return -1;
......@@ -524,7 +582,8 @@ static int lan966x_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
if (ptp == info)
continue;
if (info->pin_config[pin].func == PTP_PF_PEROUT)
if (info->pin_config[pin].func == PTP_PF_PEROUT ||
info->pin_config[pin].func == PTP_PF_EXTTS)
return -1;
}
......@@ -632,12 +691,59 @@ static int lan966x_ptp_perout(struct ptp_clock_info *ptp,
return 0;
}
static int lan966x_ptp_extts(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
struct lan966x *lan966x = phc->lan966x;
unsigned long flags;
int pin;
u32 val;
if (lan966x->ptp_ext_irq <= 0)
return -EOPNOTSUPP;
/* Reject requests with unsupported flags */
if (rq->extts.flags & ~(PTP_ENABLE_FEATURE |
PTP_RISING_EDGE |
PTP_STRICT_FLAGS))
return -EOPNOTSUPP;
pin = ptp_find_pin(phc->clock, PTP_PF_EXTTS, rq->extts.index);
if (pin == -1 || pin >= LAN966X_PHC_PINS_NUM)
return -EINVAL;
spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_SAVE) |
PTP_PIN_CFG_PIN_SYNC_SET(on ? 3 : 0) |
PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
PTP_PIN_CFG_PIN_SELECT_SET(pin),
PTP_PIN_CFG_PIN_ACTION |
PTP_PIN_CFG_PIN_SYNC |
PTP_PIN_CFG_PIN_DOM |
PTP_PIN_CFG_PIN_SELECT,
lan966x, PTP_PIN_CFG(pin));
val = lan_rd(lan966x, PTP_PIN_INTR_ENA);
if (on)
val |= BIT(pin);
else
val &= ~BIT(pin);
lan_wr(val, lan966x, PTP_PIN_INTR_ENA);
spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
return 0;
}
static int lan966x_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
switch (rq->type) {
case PTP_CLK_REQ_PEROUT:
return lan966x_ptp_perout(ptp, rq, on);
case PTP_CLK_REQ_EXTTS:
return lan966x_ptp_extts(ptp, rq, on);
default:
return -EOPNOTSUPP;
}
......@@ -656,6 +762,7 @@ static struct ptp_clock_info lan966x_ptp_clock_info = {
.verify = lan966x_ptp_verify,
.enable = lan966x_ptp_enable,
.n_per_out = LAN966X_PHC_PINS_NUM,
.n_ext_ts = LAN966X_PHC_PINS_NUM,
.n_pins = LAN966X_PHC_PINS_NUM,
};
......
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