Commit 95ccb041 authored by David S. Miller's avatar David S. Miller

Merge branch 'lan966x-ptp-programmable-pins'

Horatiu Vultur says:

====================
net: lan966x: Add support for PTP programmable pins

Lan966x has 8 PTP programmable pins. The last pin is hardcoded to be used
by PHC0 and all the rest are shareable between the PHCs. The PTP pins can
implement both extts and perout functions.

v1->v2:
- use ptp_find_pin_unlocked instead of ptp_find_pin inside the irq handler.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 124de271 f3d8e0a9
......@@ -39,6 +39,7 @@ properties:
- description: frame dma based extraction
- description: analyzer interrupt
- description: ptp interrupt
- description: ptp external interrupt
interrupt-names:
minItems: 1
......@@ -47,6 +48,7 @@ properties:
- const: fdma
- const: ana
- const: ptp
- const: ptp-ext
resets:
items:
......
......@@ -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);
......
......@@ -56,6 +56,7 @@
#define LAN966X_PHC_COUNT 3
#define LAN966X_PHC_PORT 0
#define LAN966X_PHC_PINS_NUM 7
#define IFH_REW_OP_NOOP 0x0
#define IFH_REW_OP_ONE_STEP_PTP 0x3
......@@ -177,6 +178,7 @@ struct lan966x_stat_layout {
struct lan966x_phc {
struct ptp_clock *clock;
struct ptp_clock_info info;
struct ptp_pin_desc pins[LAN966X_PHC_PINS_NUM];
struct hwtstamp_config hwtstamp_config;
struct lan966x *lan966x;
u8 index;
......@@ -231,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;
......@@ -392,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);
......
......@@ -16,7 +16,7 @@
*/
#define LAN966X_1PPB_FORMAT 3480517749LL
#define TOD_ACC_PIN 0x5
#define TOD_ACC_PIN 0x7
enum {
PTP_PIN_ACTION_IDLE = 0,
......@@ -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);
......@@ -493,6 +550,207 @@ static int lan966x_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
return 0;
}
static int lan966x_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
enum ptp_pin_function func, unsigned int chan)
{
struct lan966x_phc *phc = container_of(ptp, struct lan966x_phc, info);
struct lan966x *lan966x = phc->lan966x;
struct ptp_clock_info *info;
int i;
/* Currently support only 1 channel */
if (chan != 0)
return -1;
switch (func) {
case PTP_PF_NONE:
case PTP_PF_PEROUT:
case PTP_PF_EXTTS:
break;
default:
return -1;
}
/* The PTP pins are shared by all the PHC. So it is required to see if
* the pin is connected to another PHC. The pin is connected to another
* PHC if that pin already has a function on that PHC.
*/
for (i = 0; i < LAN966X_PHC_COUNT; ++i) {
info = &lan966x->phc[i].info;
/* Ignore the check with ourself */
if (ptp == info)
continue;
if (info->pin_config[pin].func == PTP_PF_PEROUT ||
info->pin_config[pin].func == PTP_PF_EXTTS)
return -1;
}
return 0;
}
static int lan966x_ptp_perout(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;
struct timespec64 ts_phase, ts_period;
unsigned long flags;
s64 wf_high, wf_low;
bool pps = false;
int pin;
if (rq->perout.flags & ~(PTP_PEROUT_DUTY_CYCLE |
PTP_PEROUT_PHASE))
return -EOPNOTSUPP;
pin = ptp_find_pin(phc->clock, PTP_PF_PEROUT, rq->perout.index);
if (pin == -1 || pin >= LAN966X_PHC_PINS_NUM)
return -EINVAL;
if (!on) {
spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_IDLE) |
PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
PTP_PIN_CFG_PIN_SYNC_SET(0),
PTP_PIN_CFG_PIN_ACTION |
PTP_PIN_CFG_PIN_DOM |
PTP_PIN_CFG_PIN_SYNC,
lan966x, PTP_PIN_CFG(pin));
spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
return 0;
}
if (rq->perout.period.sec == 1 &&
rq->perout.period.nsec == 0)
pps = true;
if (rq->perout.flags & PTP_PEROUT_PHASE) {
ts_phase.tv_sec = rq->perout.phase.sec;
ts_phase.tv_nsec = rq->perout.phase.nsec;
} else {
ts_phase.tv_sec = rq->perout.start.sec;
ts_phase.tv_nsec = rq->perout.start.nsec;
}
if (ts_phase.tv_sec || (ts_phase.tv_nsec && !pps)) {
dev_warn(lan966x->dev,
"Absolute time not supported!\n");
return -EINVAL;
}
if (rq->perout.flags & PTP_PEROUT_DUTY_CYCLE) {
struct timespec64 ts_on;
ts_on.tv_sec = rq->perout.on.sec;
ts_on.tv_nsec = rq->perout.on.nsec;
wf_high = timespec64_to_ns(&ts_on);
} else {
wf_high = 5000;
}
if (pps) {
spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
lan_wr(PTP_WF_LOW_PERIOD_PIN_WFL(ts_phase.tv_nsec),
lan966x, PTP_WF_LOW_PERIOD(pin));
lan_wr(PTP_WF_HIGH_PERIOD_PIN_WFH(wf_high),
lan966x, PTP_WF_HIGH_PERIOD(pin));
lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_CLOCK) |
PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
PTP_PIN_CFG_PIN_SYNC_SET(3),
PTP_PIN_CFG_PIN_ACTION |
PTP_PIN_CFG_PIN_DOM |
PTP_PIN_CFG_PIN_SYNC,
lan966x, PTP_PIN_CFG(pin));
spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
return 0;
}
ts_period.tv_sec = rq->perout.period.sec;
ts_period.tv_nsec = rq->perout.period.nsec;
wf_low = timespec64_to_ns(&ts_period);
wf_low -= wf_high;
spin_lock_irqsave(&lan966x->ptp_clock_lock, flags);
lan_wr(PTP_WF_LOW_PERIOD_PIN_WFL(wf_low),
lan966x, PTP_WF_LOW_PERIOD(pin));
lan_wr(PTP_WF_HIGH_PERIOD_PIN_WFH(wf_high),
lan966x, PTP_WF_HIGH_PERIOD(pin));
lan_rmw(PTP_PIN_CFG_PIN_ACTION_SET(PTP_PIN_ACTION_CLOCK) |
PTP_PIN_CFG_PIN_DOM_SET(phc->index) |
PTP_PIN_CFG_PIN_SYNC_SET(0),
PTP_PIN_CFG_PIN_ACTION |
PTP_PIN_CFG_PIN_DOM |
PTP_PIN_CFG_PIN_SYNC,
lan966x, PTP_PIN_CFG(pin));
spin_unlock_irqrestore(&lan966x->ptp_clock_lock, flags);
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;
}
return 0;
}
static struct ptp_clock_info lan966x_ptp_clock_info = {
.owner = THIS_MODULE,
.name = "lan966x ptp",
......@@ -501,6 +759,11 @@ static struct ptp_clock_info lan966x_ptp_clock_info = {
.settime64 = lan966x_ptp_settime64,
.adjtime = lan966x_ptp_adjtime,
.adjfine = lan966x_ptp_adjfine,
.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,
};
static int lan966x_ptp_phc_init(struct lan966x *lan966x,
......@@ -508,8 +771,19 @@ static int lan966x_ptp_phc_init(struct lan966x *lan966x,
struct ptp_clock_info *clock_info)
{
struct lan966x_phc *phc = &lan966x->phc[index];
struct ptp_pin_desc *p;
int i;
for (i = 0; i < LAN966X_PHC_PINS_NUM; i++) {
p = &phc->pins[i];
snprintf(p->name, sizeof(p->name), "pin%d", i);
p->index = i;
p->func = PTP_PF_NONE;
}
phc->info = *clock_info;
phc->info.pin_config = &phc->pins[0];
phc->clock = ptp_clock_register(&phc->info, lan966x->dev);
if (IS_ERR(phc->clock))
return PTR_ERR(phc->clock);
......
......@@ -684,6 +684,24 @@ enum lan966x_target {
/* FDMA:FDMA:FDMA_ERRORS */
#define FDMA_ERRORS __REG(TARGET_FDMA, 0, 1, 8, 0, 1, 428, 412, 0, 1, 4)
/* PTP:PTP_CFG:PTP_PIN_INTR */
#define PTP_PIN_INTR __REG(TARGET_PTP, 0, 1, 512, 0, 1, 16, 0, 0, 1, 4)
#define PTP_PIN_INTR_INTR_PTP GENMASK(7, 0)
#define PTP_PIN_INTR_INTR_PTP_SET(x)\
FIELD_PREP(PTP_PIN_INTR_INTR_PTP, x)
#define PTP_PIN_INTR_INTR_PTP_GET(x)\
FIELD_GET(PTP_PIN_INTR_INTR_PTP, x)
/* PTP:PTP_CFG:PTP_PIN_INTR_ENA */
#define PTP_PIN_INTR_ENA __REG(TARGET_PTP, 0, 1, 512, 0, 1, 16, 4, 0, 1, 4)
#define PTP_PIN_INTR_ENA_INTR_ENA GENMASK(7, 0)
#define PTP_PIN_INTR_ENA_INTR_ENA_SET(x)\
FIELD_PREP(PTP_PIN_INTR_ENA_INTR_ENA, x)
#define PTP_PIN_INTR_ENA_INTR_ENA_GET(x)\
FIELD_GET(PTP_PIN_INTR_ENA_INTR_ENA, x)
/* PTP:PTP_CFG:PTP_DOM_CFG */
#define PTP_DOM_CFG __REG(TARGET_PTP, 0, 1, 512, 0, 1, 16, 12, 0, 1, 4)
......@@ -717,6 +735,12 @@ enum lan966x_target {
#define PTP_PIN_CFG_PIN_SYNC_GET(x)\
FIELD_GET(PTP_PIN_CFG_PIN_SYNC, x)
#define PTP_PIN_CFG_PIN_SELECT GENMASK(23, 21)
#define PTP_PIN_CFG_PIN_SELECT_SET(x)\
FIELD_PREP(PTP_PIN_CFG_PIN_SELECT, x)
#define PTP_PIN_CFG_PIN_SELECT_GET(x)\
FIELD_GET(PTP_PIN_CFG_PIN_SELECT, x)
#define PTP_PIN_CFG_PIN_DOM GENMASK(17, 16)
#define PTP_PIN_CFG_PIN_DOM_SET(x)\
FIELD_PREP(PTP_PIN_CFG_PIN_DOM, x)
......@@ -744,6 +768,22 @@ enum lan966x_target {
#define PTP_TOD_NSEC_TOD_NSEC_GET(x)\
FIELD_GET(PTP_TOD_NSEC_TOD_NSEC, x)
/* PTP:PTP_PINS:WF_HIGH_PERIOD */
#define PTP_WF_HIGH_PERIOD(g) __REG(TARGET_PTP,\
0, 1, 0, g, 8, 64, 24, 0, 1, 4)
#define PTP_WF_HIGH_PERIOD_PIN_WFH(x) ((x) & GENMASK(29, 0))
#define PTP_WF_HIGH_PERIOD_PIN_WFH_M GENMASK(29, 0)
#define PTP_WF_HIGH_PERIOD_PIN_WFH_X(x) ((x) & GENMASK(29, 0))
/* PTP:PTP_PINS:WF_LOW_PERIOD */
#define PTP_WF_LOW_PERIOD(g) __REG(TARGET_PTP,\
0, 1, 0, g, 8, 64, 28, 0, 1, 4)
#define PTP_WF_LOW_PERIOD_PIN_WFL(x) ((x) & GENMASK(29, 0))
#define PTP_WF_LOW_PERIOD_PIN_WFL_M GENMASK(29, 0)
#define PTP_WF_LOW_PERIOD_PIN_WFL_X(x) ((x) & GENMASK(29, 0))
/* PTP:PTP_TS_FIFO:PTP_TWOSTEP_CTRL */
#define PTP_TWOSTEP_CTRL __REG(TARGET_PTP, 0, 1, 612, 0, 1, 12, 0, 0, 1, 4)
......
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