Skip to content

Commit 6f46fc9

Browse files
committed
Merge branch 'am65x-ptp'
Diogo Ivo says ==================== Enable PTP timestamping/PPS for AM65x SR1.0 devices This patch series enables support for PTP in AM65x SR1.0 devices. This feature relies heavily on the Industrial Ethernet Peripheral (IEP) hardware module, which implements a hardware counter through which time is kept. This hardware block is the basis for exposing a PTP hardware clock to userspace and for issuing timestamps for incoming/outgoing packets, allowing for time synchronization. The IEP also has compare registers that fire an interrupt when the counter reaches the value stored in a compare register. This feature allows us to support PPS events in the kernel. The changes are separated into five patches: - PATCH 01/05: Register SR1.0 devices with the IEP infrastructure to expose a PHC clock to userspace, allowing time to be adjusted using standard PTP tools. The code for issuing/ collecting packet timestamps is already present in the current state of the driver, so only this needs to be done. - PATCH 02/05: Remove unnecessary spinlock synchronization. - PATCH 03/05: Document IEP interrupt in DT binding. - PATCH 04/05: Add support for IEP compare event/interrupt handling to enable PPS events. - PATCH 05/05: Add the interrupts to the IOT2050 device tree. Currently every compare event generates two interrupts, the first corresponding to the actual event and the second being a spurious but otherwise harmless interrupt. The root cause of this has been identified and has been solved in the platform's SDK. A forward port of the SDK's patches also fixes the problem in upstream but is not included here since it's upstreaming is out of the scope of this series. If someone from TI would be willing to chime in and help get the interrupt changes upstream that would be great! Signed-off-by: Diogo Ivo <[email protected]> --- Changes in v4: - Remove unused 'flags' variables in patch 02/05 - Add patch 03/05 describing IEP interrupt in DT binding - Link to v3: https://lore.kernel.org/r/[email protected] Changes in v3: - Collect Reviewed-by tags - Add patch 02/04 removing spinlocks from IEP driver - Use mutex-based synchronization when accessing HW registers - Link to v2: https://lore.kernel.org/r/[email protected] Changes in v2: - Collect Reviewed-by tags - PATCH 01/03: Limit line length to 80 characters - PATCH 02/03: Proceed with limited functionality if getting IRQ fails, limit line length to 80 characters - Link to v1: https://lore.kernel.org/r/[email protected] ==================== Signed-off-by: David S. Miller <[email protected]>
2 parents 9f1f70d + 71be118 commit 6f46fc9

File tree

4 files changed

+145
-15
lines changed

4 files changed

+145
-15
lines changed

Documentation/devicetree/bindings/net/ti,icss-iep.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ properties:
2828
maxItems: 1
2929
description: phandle to the IEP source clock
3030

31+
interrupts:
32+
maxItems: 1
33+
description:
34+
Interrupt specifier for capture/compare IRQ.
35+
36+
interrupt-names:
37+
items:
38+
- const: iep_cap_cmp
39+
3140
required:
3241
- compatible
3342
- reg

arch/arm64/boot/dts/ti/k3-am65-iot2050-common-pg1.dtsi

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,15 @@
7373
"rx0", "rx1",
7474
"rxmgm0", "rxmgm1";
7575
};
76+
77+
&icssg0_iep0 {
78+
interrupt-parent = <&icssg0_intc>;
79+
interrupts = <7 7 7>;
80+
interrupt-names = "iep_cap_cmp";
81+
};
82+
83+
&icssg0_iep1 {
84+
interrupt-parent = <&icssg0_intc>;
85+
interrupts = <56 8 8>;
86+
interrupt-names = "iep_cap_cmp";
87+
};

drivers/net/ethernet/ti/icssg/icss_iep.c

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/timekeeping.h>
1818
#include <linux/interrupt.h>
1919
#include <linux/of_irq.h>
20+
#include <linux/workqueue.h>
2021

2122
#include "icss_iep.h"
2223

@@ -110,7 +111,6 @@ struct icss_iep {
110111
struct ptp_clock_info ptp_info;
111112
struct ptp_clock *ptp_clock;
112113
struct mutex ptp_clk_mutex; /* PHC access serializer */
113-
spinlock_t irq_lock; /* CMP IRQ vs icss_iep_ptp_enable access */
114114
u32 def_inc;
115115
s16 slow_cmp_inc;
116116
u32 slow_cmp_count;
@@ -122,6 +122,7 @@ struct icss_iep {
122122
int cap_cmp_irq;
123123
u64 period;
124124
u32 latch_enable;
125+
struct work_struct work;
125126
};
126127

127128
/**
@@ -192,14 +193,11 @@ static void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns)
192193
*/
193194
static void icss_iep_settime(struct icss_iep *iep, u64 ns)
194195
{
195-
unsigned long flags;
196-
197196
if (iep->ops && iep->ops->settime) {
198197
iep->ops->settime(iep->clockops_data, ns);
199198
return;
200199
}
201200

202-
spin_lock_irqsave(&iep->irq_lock, flags);
203201
if (iep->pps_enabled || iep->perout_enabled)
204202
writel(0, iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]);
205203

@@ -210,7 +208,6 @@ static void icss_iep_settime(struct icss_iep *iep, u64 ns)
210208
writel(IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN,
211209
iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]);
212210
}
213-
spin_unlock_irqrestore(&iep->irq_lock, flags);
214211
}
215212

216213
/**
@@ -546,7 +543,6 @@ static int icss_iep_perout_enable_hw(struct icss_iep *iep,
546543
static int icss_iep_perout_enable(struct icss_iep *iep,
547544
struct ptp_perout_request *req, int on)
548545
{
549-
unsigned long flags;
550546
int ret = 0;
551547

552548
mutex_lock(&iep->ptp_clk_mutex);
@@ -559,23 +555,71 @@ static int icss_iep_perout_enable(struct icss_iep *iep,
559555
if (iep->perout_enabled == !!on)
560556
goto exit;
561557

562-
spin_lock_irqsave(&iep->irq_lock, flags);
563558
ret = icss_iep_perout_enable_hw(iep, req, on);
564559
if (!ret)
565560
iep->perout_enabled = !!on;
566-
spin_unlock_irqrestore(&iep->irq_lock, flags);
567561

568562
exit:
569563
mutex_unlock(&iep->ptp_clk_mutex);
570564

571565
return ret;
572566
}
573567

568+
static void icss_iep_cap_cmp_work(struct work_struct *work)
569+
{
570+
struct icss_iep *iep = container_of(work, struct icss_iep, work);
571+
const u32 *reg_offs = iep->plat_data->reg_offs;
572+
struct ptp_clock_event pevent;
573+
unsigned int val;
574+
u64 ns, ns_next;
575+
576+
mutex_lock(&iep->ptp_clk_mutex);
577+
578+
ns = readl(iep->base + reg_offs[ICSS_IEP_CMP1_REG0]);
579+
if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT) {
580+
val = readl(iep->base + reg_offs[ICSS_IEP_CMP1_REG1]);
581+
ns |= (u64)val << 32;
582+
}
583+
/* set next event */
584+
ns_next = ns + iep->period;
585+
writel(lower_32_bits(ns_next),
586+
iep->base + reg_offs[ICSS_IEP_CMP1_REG0]);
587+
if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
588+
writel(upper_32_bits(ns_next),
589+
iep->base + reg_offs[ICSS_IEP_CMP1_REG1]);
590+
591+
pevent.pps_times.ts_real = ns_to_timespec64(ns);
592+
pevent.type = PTP_CLOCK_PPSUSR;
593+
pevent.index = 0;
594+
ptp_clock_event(iep->ptp_clock, &pevent);
595+
dev_dbg(iep->dev, "IEP:pps ts: %llu next:%llu:\n", ns, ns_next);
596+
597+
mutex_unlock(&iep->ptp_clk_mutex);
598+
}
599+
600+
static irqreturn_t icss_iep_cap_cmp_irq(int irq, void *dev_id)
601+
{
602+
struct icss_iep *iep = (struct icss_iep *)dev_id;
603+
const u32 *reg_offs = iep->plat_data->reg_offs;
604+
unsigned int val;
605+
606+
val = readl(iep->base + reg_offs[ICSS_IEP_CMP_STAT_REG]);
607+
/* The driver only enables CMP1 */
608+
if (val & BIT(1)) {
609+
/* Clear the event */
610+
writel(BIT(1), iep->base + reg_offs[ICSS_IEP_CMP_STAT_REG]);
611+
if (iep->pps_enabled || iep->perout_enabled)
612+
schedule_work(&iep->work);
613+
return IRQ_HANDLED;
614+
}
615+
616+
return IRQ_NONE;
617+
}
618+
574619
static int icss_iep_pps_enable(struct icss_iep *iep, int on)
575620
{
576621
struct ptp_clock_request rq;
577622
struct timespec64 ts;
578-
unsigned long flags;
579623
int ret = 0;
580624
u64 ns;
581625

@@ -589,8 +633,6 @@ static int icss_iep_pps_enable(struct icss_iep *iep, int on)
589633
if (iep->pps_enabled == !!on)
590634
goto exit;
591635

592-
spin_lock_irqsave(&iep->irq_lock, flags);
593-
594636
rq.perout.index = 0;
595637
if (on) {
596638
ns = icss_iep_gettime(iep, NULL);
@@ -602,13 +644,13 @@ static int icss_iep_pps_enable(struct icss_iep *iep, int on)
602644
ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);
603645
} else {
604646
ret = icss_iep_perout_enable_hw(iep, &rq.perout, on);
647+
if (iep->cap_cmp_irq)
648+
cancel_work_sync(&iep->work);
605649
}
606650

607651
if (!ret)
608652
iep->pps_enabled = !!on;
609653

610-
spin_unlock_irqrestore(&iep->irq_lock, flags);
611-
612654
exit:
613655
mutex_unlock(&iep->ptp_clk_mutex);
614656

@@ -777,6 +819,8 @@ int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops,
777819
if (iep->ops && iep->ops->perout_enable) {
778820
iep->ptp_info.n_per_out = 1;
779821
iep->ptp_info.pps = 1;
822+
} else if (iep->cap_cmp_irq) {
823+
iep->ptp_info.pps = 1;
780824
}
781825

782826
if (iep->ops && iep->ops->extts_enable)
@@ -817,6 +861,7 @@ static int icss_iep_probe(struct platform_device *pdev)
817861
struct device *dev = &pdev->dev;
818862
struct icss_iep *iep;
819863
struct clk *iep_clk;
864+
int ret, irq;
820865

821866
iep = devm_kzalloc(dev, sizeof(*iep), GFP_KERNEL);
822867
if (!iep)
@@ -827,6 +872,22 @@ static int icss_iep_probe(struct platform_device *pdev)
827872
if (IS_ERR(iep->base))
828873
return -ENODEV;
829874

875+
irq = platform_get_irq_byname_optional(pdev, "iep_cap_cmp");
876+
if (irq == -EPROBE_DEFER)
877+
return irq;
878+
879+
if (irq > 0) {
880+
ret = devm_request_irq(dev, irq, icss_iep_cap_cmp_irq,
881+
IRQF_TRIGGER_HIGH, "iep_cap_cmp", iep);
882+
if (ret) {
883+
dev_info(iep->dev, "cap_cmp irq request failed: %x\n",
884+
ret);
885+
} else {
886+
iep->cap_cmp_irq = irq;
887+
INIT_WORK(&iep->work, icss_iep_cap_cmp_work);
888+
}
889+
}
890+
830891
iep_clk = devm_clk_get(dev, NULL);
831892
if (IS_ERR(iep_clk))
832893
return PTR_ERR(iep_clk);
@@ -853,7 +914,6 @@ static int icss_iep_probe(struct platform_device *pdev)
853914

854915
iep->ptp_info = icss_iep_ptp_info;
855916
mutex_init(&iep->ptp_clk_mutex);
856-
spin_lock_init(&iep->irq_lock);
857917
dev_set_drvdata(dev, iep);
858918
icss_iep_disable(iep);
859919

drivers/net/ethernet/ti/icssg/icssg_prueth_sr1.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,16 +1011,44 @@ static int prueth_probe(struct platform_device *pdev)
10111011
dev_dbg(dev, "sram: pa %llx va %p size %zx\n", prueth->msmcram.pa,
10121012
prueth->msmcram.va, prueth->msmcram.size);
10131013

1014+
prueth->iep0 = icss_iep_get_idx(np, 0);
1015+
if (IS_ERR(prueth->iep0)) {
1016+
ret = dev_err_probe(dev, PTR_ERR(prueth->iep0),
1017+
"iep0 get failed\n");
1018+
goto free_pool;
1019+
}
1020+
1021+
prueth->iep1 = icss_iep_get_idx(np, 1);
1022+
if (IS_ERR(prueth->iep1)) {
1023+
ret = dev_err_probe(dev, PTR_ERR(prueth->iep1),
1024+
"iep1 get failed\n");
1025+
goto put_iep0;
1026+
}
1027+
1028+
ret = icss_iep_init(prueth->iep0, NULL, NULL, 0);
1029+
if (ret) {
1030+
dev_err_probe(dev, ret, "failed to init iep0\n");
1031+
goto put_iep;
1032+
}
1033+
1034+
ret = icss_iep_init(prueth->iep1, NULL, NULL, 0);
1035+
if (ret) {
1036+
dev_err_probe(dev, ret, "failed to init iep1\n");
1037+
goto exit_iep0;
1038+
}
1039+
10141040
if (eth0_node) {
10151041
ret = prueth_netdev_init(prueth, eth0_node);
10161042
if (ret) {
10171043
dev_err_probe(dev, ret, "netdev init %s failed\n",
10181044
eth0_node->name);
1019-
goto free_pool;
1045+
goto exit_iep;
10201046
}
10211047

10221048
if (of_find_property(eth0_node, "ti,half-duplex-capable", NULL))
10231049
prueth->emac[PRUETH_MAC0]->half_duplex = 1;
1050+
1051+
prueth->emac[PRUETH_MAC0]->iep = prueth->iep0;
10241052
}
10251053

10261054
if (eth1_node) {
@@ -1033,6 +1061,8 @@ static int prueth_probe(struct platform_device *pdev)
10331061

10341062
if (of_find_property(eth1_node, "ti,half-duplex-capable", NULL))
10351063
prueth->emac[PRUETH_MAC1]->half_duplex = 1;
1064+
1065+
prueth->emac[PRUETH_MAC1]->iep = prueth->iep1;
10361066
}
10371067

10381068
/* register the network devices */
@@ -1091,6 +1121,19 @@ static int prueth_probe(struct platform_device *pdev)
10911121
prueth_netdev_exit(prueth, eth_node);
10921122
}
10931123

1124+
exit_iep:
1125+
icss_iep_exit(prueth->iep1);
1126+
exit_iep0:
1127+
icss_iep_exit(prueth->iep0);
1128+
1129+
put_iep:
1130+
icss_iep_put(prueth->iep1);
1131+
1132+
put_iep0:
1133+
icss_iep_put(prueth->iep0);
1134+
prueth->iep0 = NULL;
1135+
prueth->iep1 = NULL;
1136+
10941137
free_pool:
10951138
gen_pool_free(prueth->sram_pool,
10961139
(unsigned long)prueth->msmcram.va, msmc_ram_size);
@@ -1138,6 +1181,12 @@ static void prueth_remove(struct platform_device *pdev)
11381181
prueth_netdev_exit(prueth, eth_node);
11391182
}
11401183

1184+
icss_iep_exit(prueth->iep1);
1185+
icss_iep_exit(prueth->iep0);
1186+
1187+
icss_iep_put(prueth->iep1);
1188+
icss_iep_put(prueth->iep0);
1189+
11411190
gen_pool_free(prueth->sram_pool,
11421191
(unsigned long)prueth->msmcram.va,
11431192
MSMC_RAM_SIZE_SR1);

0 commit comments

Comments
 (0)