Skip to content

Commit 0b26df8

Browse files
Roger QuadrosNipaLocal
authored andcommitted
net: ti: prueth: Adds PTP OC Support for AM335x and AM437x
PRU-ICSS IEP module, which is capable of timestamping RX and TX packets at HW level, is used for time synchronization by PTP4L. This change includes interaction between firmware/driver and user application (ptp4l) with required packet timestamps. RX SOF timestamp comes along with packet and firmware will rise interrupt with TX SOF timestamp after pushing the packet on to the wire. IEP driver available in upstream linux as part of ICSSG assumes 64-bit timestamp value from firmware. Enhanced the IEP driver to support the legacy 32-bit timestamp conversion to 64-bit timestamp by using 2 fields as below: - 32-bit HW timestamp from SOF event in ns - Seconds value maintained in driver. Currently ordinary clock (OC) configuration has been validated with Linux ptp4l. Signed-off-by: Roger Quadros <[email protected]> Signed-off-by: Andrew F. Davis <[email protected]> Signed-off-by: Basharath Hussain Khaja <[email protected]> Signed-off-by: Parvathi Pudi <[email protected]> Signed-off-by: NipaLocal <nipa@local>
1 parent 0a9a1ee commit 0b26df8

File tree

4 files changed

+230
-4
lines changed

4 files changed

+230
-4
lines changed

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

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
#include <linux/of.h>
1515
#include <linux/of_platform.h>
1616
#include <linux/platform_device.h>
17+
#include <linux/timecounter.h>
18+
#include <linux/clocksource.h>
1719
#include <linux/timekeeping.h>
1820
#include <linux/interrupt.h>
1921
#include <linux/of_irq.h>
2022
#include <linux/workqueue.h>
2123

2224
#include "icss_iep.h"
25+
#include "../icssm/icssm_prueth_ptp.h"
2326

2427
#define IEP_MAX_DEF_INC 0xf
2528
#define IEP_MAX_COMPEN_INC 0xfff
@@ -53,6 +56,14 @@
5356
#define IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(n) BIT(LATCH_INDEX(n))
5457
#define IEP_CAP_CFG_CAP_ASYNC_EN(n) BIT(LATCH_INDEX(n) + 10)
5558

59+
#define IEP_TC_DEFAULT_SHIFT 28
60+
#define IEP_TC_INCR5_MULT BIT(28)
61+
62+
/* Polling period - how often iep_overflow_check() is called */
63+
#define IEP_OVERFLOW_CHECK_PERIOD_MS 50
64+
65+
#define TIMESYNC_SECONDS_COUNT_SIZE 6
66+
5667
/**
5768
* icss_iep_get_count_hi() - Get the upper 32 bit IEP counter
5869
* @iep: Pointer to structure representing IEP.
@@ -87,6 +98,28 @@ int icss_iep_get_count_low(struct icss_iep *iep)
8798
}
8899
EXPORT_SYMBOL_GPL(icss_iep_get_count_low);
89100

101+
static u64 icss_iep_get_count32(struct icss_iep *iep)
102+
{
103+
void __iomem *sram = iep->sram;
104+
u64 v_sec = 0;
105+
u32 v_ns = 0;
106+
u64 v = 0;
107+
108+
v_ns = icss_iep_get_count_low(iep);
109+
memcpy_fromio(&v_sec, sram + TIMESYNC_SECONDS_COUNT_OFFSET,
110+
TIMESYNC_SECONDS_COUNT_SIZE);
111+
v = (v_sec * NSEC_PER_SEC) + v_ns;
112+
113+
return v;
114+
}
115+
116+
static u64 icss_iep_cc_read(const struct cyclecounter *cc)
117+
{
118+
struct icss_iep *iep = container_of(cc, struct icss_iep, cc);
119+
120+
return icss_iep_get_count32(iep);
121+
}
122+
90123
/**
91124
* icss_iep_get_ptp_clock_idx() - Get PTP clock index using IEP driver
92125
* @iep: Pointer to structure representing IEP.
@@ -280,6 +313,78 @@ static void icss_iep_set_slow_compensation_count(struct icss_iep *iep,
280313
regmap_write(iep->map, ICSS_IEP_SLOW_COMPEN_REG, compen_count);
281314
}
282315

316+
/* PTP PHC operations */
317+
static int icss_iep_ptp_adjfine_v1(struct ptp_clock_info *ptp, long scaled_ppm)
318+
{
319+
struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
320+
s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
321+
struct timespec64 ts;
322+
int neg_adj = 0;
323+
u32 diff, mult;
324+
u64 adj;
325+
326+
mutex_lock(&iep->ptp_clk_mutex);
327+
328+
if (ppb < 0) {
329+
neg_adj = 1;
330+
ppb = -ppb;
331+
}
332+
mult = iep->cc_mult;
333+
adj = mult;
334+
adj *= ppb;
335+
diff = div_u64(adj, 1000000000ULL);
336+
337+
ts = ns_to_timespec64(timecounter_read(&iep->tc));
338+
pr_debug("iep ptp adjfine check at %lld.%09lu\n", ts.tv_sec,
339+
ts.tv_nsec);
340+
341+
iep->cc.mult = neg_adj ? mult - diff : mult + diff;
342+
343+
mutex_unlock(&iep->ptp_clk_mutex);
344+
345+
return 0;
346+
}
347+
348+
static int icss_iep_ptp_adjtime_v1(struct ptp_clock_info *ptp, s64 delta)
349+
{
350+
struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
351+
352+
mutex_lock(&iep->ptp_clk_mutex);
353+
timecounter_adjtime(&iep->tc, delta);
354+
mutex_unlock(&iep->ptp_clk_mutex);
355+
356+
return 0;
357+
}
358+
359+
static int icss_iep_ptp_gettimeex_v1(struct ptp_clock_info *ptp,
360+
struct timespec64 *ts,
361+
struct ptp_system_timestamp *sts)
362+
{
363+
struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
364+
u64 ns;
365+
366+
mutex_lock(&iep->ptp_clk_mutex);
367+
ns = timecounter_read(&iep->tc);
368+
*ts = ns_to_timespec64(ns);
369+
mutex_unlock(&iep->ptp_clk_mutex);
370+
371+
return 0;
372+
}
373+
374+
static int icss_iep_ptp_settime_v1(struct ptp_clock_info *ptp,
375+
const struct timespec64 *ts)
376+
{
377+
struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
378+
u64 ns;
379+
380+
mutex_lock(&iep->ptp_clk_mutex);
381+
ns = timespec64_to_ns(ts);
382+
timecounter_init(&iep->tc, &iep->cc, ns);
383+
mutex_unlock(&iep->ptp_clk_mutex);
384+
385+
return 0;
386+
}
387+
283388
/* PTP PHC operations */
284389
static int icss_iep_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
285390
{
@@ -669,6 +774,17 @@ static int icss_iep_ptp_enable(struct ptp_clock_info *ptp,
669774
return -EOPNOTSUPP;
670775
}
671776

777+
static long icss_iep_overflow_check(struct ptp_clock_info *ptp)
778+
{
779+
struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
780+
unsigned long delay = iep->ovfl_check_period;
781+
struct timespec64 ts;
782+
783+
ts = ns_to_timespec64(timecounter_read(&iep->tc));
784+
785+
pr_debug("iep overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec);
786+
return (long)delay;
787+
}
672788
static struct ptp_clock_info icss_iep_ptp_info = {
673789
.owner = THIS_MODULE,
674790
.name = "ICSS IEP timer",
@@ -680,6 +796,18 @@ static struct ptp_clock_info icss_iep_ptp_info = {
680796
.enable = icss_iep_ptp_enable,
681797
};
682798

799+
static struct ptp_clock_info icss_iep_ptp_info_v1 = {
800+
.owner = THIS_MODULE,
801+
.name = "ICSS IEP timer",
802+
.max_adj = 10000000,
803+
.adjfine = icss_iep_ptp_adjfine_v1,
804+
.adjtime = icss_iep_ptp_adjtime_v1,
805+
.gettimex64 = icss_iep_ptp_gettimeex_v1,
806+
.settime64 = icss_iep_ptp_settime_v1,
807+
.enable = icss_iep_ptp_enable,
808+
.do_aux_work = icss_iep_overflow_check,
809+
};
810+
683811
struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx)
684812
{
685813
struct platform_device *pdev;
@@ -701,6 +829,18 @@ struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx)
701829
if (!iep)
702830
return ERR_PTR(-EPROBE_DEFER);
703831

832+
if (iep->plat_data->iep_rev == IEP_REV_V1_0) {
833+
iep->cc.shift = IEP_TC_DEFAULT_SHIFT;
834+
iep->cc.mult = IEP_TC_INCR5_MULT;
835+
836+
iep->cc.read = icss_iep_cc_read;
837+
iep->cc.mask = CLOCKSOURCE_MASK(64);
838+
839+
iep->ovfl_check_period =
840+
msecs_to_jiffies(IEP_OVERFLOW_CHECK_PERIOD_MS);
841+
iep->cc_mult = iep->cc.mult;
842+
}
843+
704844
device_lock(iep->dev);
705845
if (iep->client_np) {
706846
device_unlock(iep->dev);
@@ -795,13 +935,20 @@ int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops,
795935
icss_iep_enable(iep);
796936
icss_iep_settime(iep, ktime_get_real_ns());
797937

938+
if (iep->plat_data->iep_rev == IEP_REV_V1_0)
939+
timecounter_init(&iep->tc, &iep->cc,
940+
ktime_to_ns(ktime_get_real()));
941+
798942
iep->ptp_clock = ptp_clock_register(&iep->ptp_info, iep->dev);
799943
if (IS_ERR(iep->ptp_clock)) {
800944
ret = PTR_ERR(iep->ptp_clock);
801945
iep->ptp_clock = NULL;
802946
dev_err(iep->dev, "Failed to register ptp clk %d\n", ret);
803947
}
804948

949+
if (iep->plat_data->iep_rev == IEP_REV_V1_0)
950+
ptp_schedule_worker(iep->ptp_clock, iep->ovfl_check_period);
951+
805952
return ret;
806953
}
807954
EXPORT_SYMBOL_GPL(icss_iep_init);
@@ -879,7 +1026,11 @@ static int icss_iep_probe(struct platform_device *pdev)
8791026
return PTR_ERR(iep->map);
8801027
}
8811028

882-
iep->ptp_info = icss_iep_ptp_info;
1029+
if (iep->plat_data->iep_rev == IEP_REV_V1_0)
1030+
iep->ptp_info = icss_iep_ptp_info_v1;
1031+
else
1032+
iep->ptp_info = icss_iep_ptp_info;
1033+
8831034
mutex_init(&iep->ptp_clk_mutex);
8841035
dev_set_drvdata(dev, iep);
8851036
icss_iep_disable(iep);
@@ -1004,6 +1155,7 @@ static const struct icss_iep_plat_data am57xx_icss_iep_plat_data = {
10041155
[ICSS_IEP_SYNC_START_REG] = 0x19c,
10051156
},
10061157
.config = &am654_icss_iep_regmap_config,
1158+
.iep_rev = IEP_REV_V2_1,
10071159
};
10081160

10091161
static bool am335x_icss_iep_valid_reg(struct device *dev, unsigned int reg)
@@ -1057,6 +1209,7 @@ static const struct icss_iep_plat_data am335x_icss_iep_plat_data = {
10571209
[ICSS_IEP_SYNC_START_REG] = 0x11C,
10581210
},
10591211
.config = &am335x_icss_iep_regmap_config,
1212+
.iep_rev = IEP_REV_V1_0,
10601213
};
10611214

10621215
static const struct of_device_id icss_iep_of_match[] = {

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,29 @@ enum {
4747
ICSS_IEP_MAX_REGS,
4848
};
4949

50+
enum iep_revision {
51+
IEP_REV_V1_0 = 0,
52+
IEP_REV_V2_1
53+
};
54+
5055
/**
5156
* struct icss_iep_plat_data - Plat data to handle SoC variants
5257
* @config: Regmap configuration data
5358
* @reg_offs: register offsets to capture offset differences across SoCs
5459
* @flags: Flags to represent IEP properties
60+
* @iep_rev: IEP revision identifier.
5561
*/
5662
struct icss_iep_plat_data {
5763
const struct regmap_config *config;
5864
u32 reg_offs[ICSS_IEP_MAX_REGS];
5965
u32 flags;
66+
enum iep_revision iep_rev;
6067
};
6168

6269
struct icss_iep {
6370
struct device *dev;
6471
void __iomem *base;
72+
void __iomem *sram;
6573
const struct icss_iep_plat_data *plat_data;
6674
struct regmap *map;
6775
struct device_node *client_np;
@@ -70,6 +78,10 @@ struct icss_iep {
7078
struct ptp_clock_info ptp_info;
7179
struct ptp_clock *ptp_clock;
7280
struct mutex ptp_clk_mutex; /* PHC access serializer */
81+
u32 cc_mult; /* for the nominal frequency */
82+
struct cyclecounter cc;
83+
struct timecounter tc;
84+
unsigned long ovfl_check_period;
7385
u32 def_inc;
7486
s16 slow_cmp_inc;
7587
u32 slow_cmp_count;

drivers/net/ethernet/ti/icssm/icssm_prueth.c

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
#define TX_START_DELAY 0x40
4040
#define TX_CLK_DELAY_100M 0x6
4141

42+
#define TIMESYNC_SECONDS_BIT_MASK 0x0000ffffffffffff
43+
4244
static struct prueth_fw_offsets fw_offsets_v2_1;
4345

4446
static void icssm_prueth_set_fw_offsets(struct prueth *prueth)
@@ -642,13 +644,49 @@ irqreturn_t icssm_prueth_ptp_tx_irq_handle(int irq, void *dev)
642644
return IRQ_HANDLED;
643645
}
644646

647+
/**
648+
* icssm_iep_get_timestamp_cycles - IEP get timestamp
649+
* @iep: icss_iep structure
650+
* @mem: io memory address
651+
*
652+
* To convert the 10 byte timestamp from firmware
653+
* i.e., nanoseconds part from 32-bit IEP counter(4 bytes)
654+
* seconds part updated by firmware(rev FW_REV1_0) in SRAM
655+
* (6 bytes) into 64-bit timestamp in ns
656+
*
657+
* Return: 64-bit converted timestamp
658+
*/
659+
u64 icssm_iep_get_timestamp_cycles(struct icss_iep *iep,
660+
void __iomem *mem)
661+
{
662+
u64 cycles, cycles_sec = 0;
663+
u32 cycles_ns;
664+
665+
memcpy_fromio(&cycles_ns, mem, sizeof(cycles_ns));
666+
memcpy_fromio(&cycles_sec, mem + 4, sizeof(cycles_sec));
667+
668+
/*To get the 6 bytes seconds part*/
669+
cycles_sec = (cycles_sec & TIMESYNC_SECONDS_BIT_MASK);
670+
cycles = cycles_ns + (cycles_sec * NSEC_PER_SEC);
671+
cycles = timecounter_cyc2time(&iep->tc, cycles);
672+
673+
return cycles;
674+
}
675+
645676
static u64 icssm_prueth_ptp_ts_get(struct prueth_emac *emac, u32 ts_offs)
646677
{
647678
void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va;
648679
u64 cycles;
649680

650-
memcpy_fromio(&cycles, sram + ts_offs, sizeof(cycles));
651-
memset_io(sram + ts_offs, 0, sizeof(cycles));
681+
if (emac->prueth->fw_data->fw_rev == FW_REV_V1_0) {
682+
cycles = icssm_iep_get_timestamp_cycles(emac->prueth->iep,
683+
sram + ts_offs);
684+
/* 4 bytes of timestamp + 6 bytes of seconds counter */
685+
memset_io(sram + ts_offs, 0, 10);
686+
} else {
687+
memcpy_fromio(&cycles, sram + ts_offs, sizeof(cycles));
688+
memset_io(sram + ts_offs, 0, sizeof(cycles));
689+
}
652690

653691
return cycles;
654692
}
@@ -985,7 +1023,13 @@ int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr,
9851023
pkt_info->timestamp) {
9861024
src_addr = (void *)PTR_ALIGN((uintptr_t)src_addr,
9871025
ICSS_BLOCK_SIZE);
988-
memcpy(&ts, src_addr, sizeof(ts));
1026+
if (emac->prueth->fw_data->fw_rev == FW_REV_V1_0) {
1027+
ts = icssm_iep_get_timestamp_cycles
1028+
(emac->prueth->iep,
1029+
(void __iomem *)src_addr);
1030+
} else {
1031+
memcpy(&ts, src_addr, sizeof(ts));
1032+
}
9891033
ssh = skb_hwtstamps(skb);
9901034
memset(ssh, 0, sizeof(*ssh));
9911035
ssh->hwtstamp = ns_to_ktime(ts);
@@ -2189,6 +2233,9 @@ static int icssm_prueth_probe(struct platform_device *pdev)
21892233
goto netdev_exit;
21902234
}
21912235

2236+
if (prueth->fw_data->fw_rev == FW_REV_V1_0)
2237+
prueth->iep->sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va;
2238+
21922239
/* Make rx interrupt pacing optional so that users can use ECAP for
21932240
* other use cases if needed
21942241
*/
@@ -2396,6 +2443,7 @@ static struct prueth_private_data am335x_prueth_pdata = {
23962443
.fw_name[PRUSS_ETHTYPE_EMAC] =
23972444
"ti-pruss/am335x-pru1-prueth-fw.elf",
23982445
},
2446+
.fw_rev = FW_REV_V1_0,
23992447
};
24002448

24012449
/* AM437x SoC-specific firmware data */
@@ -2409,6 +2457,7 @@ static struct prueth_private_data am437x_prueth_pdata = {
24092457
.fw_name[PRUSS_ETHTYPE_EMAC] =
24102458
"ti-pruss/am437x-pru1-prueth-fw.elf",
24112459
},
2460+
.fw_rev = FW_REV_V1_0,
24122461
};
24132462

24142463
/* AM57xx SoC-specific firmware data */
@@ -2422,6 +2471,7 @@ static struct prueth_private_data am57xx_prueth_pdata = {
24222471
.fw_name[PRUSS_ETHTYPE_EMAC] =
24232472
"ti-pruss/am57xx-pru1-prueth-fw.elf",
24242473
},
2474+
.fw_rev = FW_REV_V2_1,
24252475
};
24262476

24272477
static const struct of_device_id prueth_dt_match[] = {

0 commit comments

Comments
 (0)