@@ -14,12 +14,15 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/timecounter.h>
+#include <linux/clocksource.h>
#include <linux/timekeeping.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/workqueue.h>
#include "icss_iep.h"
+#include "../icssm/icssm_prueth_ptp.h"
#define IEP_MAX_DEF_INC 0xf
#define IEP_MAX_COMPEN_INC 0xfff
@@ -53,6 +56,14 @@
#define IEP_CAP_CFG_CAPNR_1ST_EVENT_EN(n) BIT(LATCH_INDEX(n))
#define IEP_CAP_CFG_CAP_ASYNC_EN(n) BIT(LATCH_INDEX(n) + 10)
+#define IEP_TC_DEFAULT_SHIFT 28
+#define IEP_TC_INCR5_MULT BIT(28)
+
+/* Polling period - how often iep_overflow_check() is called */
+#define IEP_OVERFLOW_CHECK_PERIOD_MS 50
+
+#define TIMESYNC_SECONDS_COUNT_SIZE 6
+
/**
* icss_iep_get_count_hi() - Get the upper 32 bit IEP counter
* @iep: Pointer to structure representing IEP.
@@ -87,6 +98,28 @@ int icss_iep_get_count_low(struct icss_iep *iep)
}
EXPORT_SYMBOL_GPL(icss_iep_get_count_low);
+static inline u64 icss_iep_get_count32(struct icss_iep *iep)
+{
+ void __iomem *sram = iep->sram;
+ u64 v_sec = 0;
+ u32 v_ns = 0;
+ u64 v = 0;
+
+ v_ns = icss_iep_get_count_low(iep);
+ memcpy_fromio(&v_sec, sram + TIMESYNC_SECONDS_COUNT_OFFSET,
+ TIMESYNC_SECONDS_COUNT_SIZE);
+ v = (v_sec * NSEC_PER_SEC) + v_ns;
+
+ return v;
+}
+
+static u64 icss_iep_cc_read(const struct cyclecounter *cc)
+{
+ struct icss_iep *iep = container_of(cc, struct icss_iep, cc);
+
+ return icss_iep_get_count32(iep);
+}
+
/**
* icss_iep_get_ptp_clock_idx() - Get PTP clock index using IEP driver
* @iep: Pointer to structure representing IEP.
@@ -280,6 +313,78 @@ static void icss_iep_set_slow_compensation_count(struct icss_iep *iep,
regmap_write(iep->map, ICSS_IEP_SLOW_COMPEN_REG, compen_count);
}
+/* PTP PHC operations */
+static int icss_iep_ptp_adjfine_v1(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+ struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
+ s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
+ struct timespec64 ts;
+ int neg_adj = 0;
+ u32 diff, mult;
+ u64 adj;
+
+ mutex_lock(&iep->ptp_clk_mutex);
+
+ if (ppb < 0) {
+ neg_adj = 1;
+ ppb = -ppb;
+ }
+ mult = iep->cc_mult;
+ adj = mult;
+ adj *= ppb;
+ diff = div_u64(adj, 1000000000ULL);
+
+ ts = ns_to_timespec64(timecounter_read(&iep->tc));
+ pr_debug("iep ptp adjfine check at %lld.%09lu\n", ts.tv_sec,
+ ts.tv_nsec);
+
+ iep->cc.mult = neg_adj ? mult - diff : mult + diff;
+
+ mutex_unlock(&iep->ptp_clk_mutex);
+
+ return 0;
+}
+
+static int icss_iep_ptp_adjtime_v1(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
+
+ mutex_lock(&iep->ptp_clk_mutex);
+ timecounter_adjtime(&iep->tc, delta);
+ mutex_unlock(&iep->ptp_clk_mutex);
+
+ return 0;
+}
+
+static int icss_iep_ptp_gettimeex_v1(struct ptp_clock_info *ptp,
+ struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
+ u64 ns;
+
+ mutex_lock(&iep->ptp_clk_mutex);
+ ns = timecounter_read(&iep->tc);
+ *ts = ns_to_timespec64(ns);
+ mutex_unlock(&iep->ptp_clk_mutex);
+
+ return 0;
+}
+
+static int icss_iep_ptp_settime_v1(struct ptp_clock_info *ptp,
+ const struct timespec64 *ts)
+{
+ struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
+ u64 ns;
+
+ mutex_lock(&iep->ptp_clk_mutex);
+ ns = timespec64_to_ns(ts);
+ timecounter_init(&iep->tc, &iep->cc, ns);
+ mutex_unlock(&iep->ptp_clk_mutex);
+
+ return 0;
+}
+
/* PTP PHC operations */
static int icss_iep_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
@@ -674,6 +779,17 @@ static int icss_iep_ptp_enable(struct ptp_clock_info *ptp,
return -EOPNOTSUPP;
}
+static long icss_iep_overflow_check(struct ptp_clock_info *ptp)
+{
+ struct icss_iep *iep = container_of(ptp, struct icss_iep, ptp_info);
+ unsigned long delay = iep->ovfl_check_period;
+ struct timespec64 ts;
+
+ ts = ns_to_timespec64(timecounter_read(&iep->tc));
+
+ pr_debug("iep overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec);
+ return (long)delay;
+}
static struct ptp_clock_info icss_iep_ptp_info = {
.owner = THIS_MODULE,
.name = "ICSS IEP timer",
@@ -685,6 +801,18 @@ static struct ptp_clock_info icss_iep_ptp_info = {
.enable = icss_iep_ptp_enable,
};
+static struct ptp_clock_info icss_iep_ptp_info_v1 = {
+ .owner = THIS_MODULE,
+ .name = "ICSS IEP timer",
+ .max_adj = 10000000,
+ .adjfine = icss_iep_ptp_adjfine_v1,
+ .adjtime = icss_iep_ptp_adjtime_v1,
+ .gettimex64 = icss_iep_ptp_gettimeex_v1,
+ .settime64 = icss_iep_ptp_settime_v1,
+ .enable = icss_iep_ptp_enable,
+ .do_aux_work = icss_iep_overflow_check,
+};
+
struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx)
{
struct platform_device *pdev;
@@ -706,6 +834,18 @@ struct icss_iep *icss_iep_get_idx(struct device_node *np, int idx)
if (!iep)
return ERR_PTR(-EPROBE_DEFER);
+ if (iep->plat_data->iep_rev == IEP_REV_V1_0) {
+ iep->cc.shift = IEP_TC_DEFAULT_SHIFT;
+ iep->cc.mult = IEP_TC_INCR5_MULT;
+
+ iep->cc.read = icss_iep_cc_read;
+ iep->cc.mask = CLOCKSOURCE_MASK(64);
+
+ iep->ovfl_check_period =
+ msecs_to_jiffies(IEP_OVERFLOW_CHECK_PERIOD_MS);
+ iep->cc_mult = iep->cc.mult;
+ }
+
device_lock(iep->dev);
if (iep->client_np) {
device_unlock(iep->dev);
@@ -800,6 +940,10 @@ int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops,
icss_iep_enable(iep);
icss_iep_settime(iep, ktime_get_real_ns());
+ if (iep->plat_data->iep_rev == IEP_REV_V1_0)
+ timecounter_init(&iep->tc, &iep->cc,
+ ktime_to_ns(ktime_get_real()));
+
iep->ptp_clock = ptp_clock_register(&iep->ptp_info, iep->dev);
if (IS_ERR(iep->ptp_clock)) {
ret = PTR_ERR(iep->ptp_clock);
@@ -807,6 +951,9 @@ int icss_iep_init(struct icss_iep *iep, const struct icss_iep_clockops *clkops,
dev_err(iep->dev, "Failed to register ptp clk %d\n", ret);
}
+ if (iep->plat_data->iep_rev == IEP_REV_V1_0)
+ ptp_schedule_worker(iep->ptp_clock, iep->ovfl_check_period);
+
return ret;
}
EXPORT_SYMBOL_GPL(icss_iep_init);
@@ -884,7 +1031,11 @@ static int icss_iep_probe(struct platform_device *pdev)
return PTR_ERR(iep->map);
}
- iep->ptp_info = icss_iep_ptp_info;
+ if (iep->plat_data->iep_rev == IEP_REV_V1_0)
+ iep->ptp_info = icss_iep_ptp_info_v1;
+ else
+ iep->ptp_info = icss_iep_ptp_info;
+
mutex_init(&iep->ptp_clk_mutex);
dev_set_drvdata(dev, iep);
icss_iep_disable(iep);
@@ -1009,6 +1160,7 @@ static const struct icss_iep_plat_data am57xx_icss_iep_plat_data = {
[ICSS_IEP_SYNC_START_REG] = 0x19c,
},
.config = &am654_icss_iep_regmap_config,
+ .iep_rev = IEP_REV_V2_1,
};
static bool am335x_icss_iep_valid_reg(struct device *dev, unsigned int reg)
@@ -1062,6 +1214,7 @@ static const struct icss_iep_plat_data am335x_icss_iep_plat_data = {
[ICSS_IEP_SYNC_START_REG] = 0x11C,
},
.config = &am335x_icss_iep_regmap_config,
+ .iep_rev = IEP_REV_V1_0,
};
static const struct of_device_id icss_iep_of_match[] = {
@@ -47,6 +47,11 @@ enum {
ICSS_IEP_MAX_REGS,
};
+enum iep_revision {
+ IEP_REV_V1_0 = 0,
+ IEP_REV_V2_1
+};
+
/**
* struct icss_iep_plat_data - Plat data to handle SoC variants
* @config: Regmap configuration data
@@ -57,11 +62,13 @@ struct icss_iep_plat_data {
const struct regmap_config *config;
u32 reg_offs[ICSS_IEP_MAX_REGS];
u32 flags;
+ enum iep_revision iep_rev;
};
struct icss_iep {
struct device *dev;
void __iomem *base;
+ void __iomem *sram;
const struct icss_iep_plat_data *plat_data;
struct regmap *map;
struct device_node *client_np;
@@ -70,6 +77,10 @@ struct icss_iep {
struct ptp_clock_info ptp_info;
struct ptp_clock *ptp_clock;
struct mutex ptp_clk_mutex; /* PHC access serializer */
+ u32 cc_mult; /* for the nominal frequency */
+ struct cyclecounter cc;
+ struct timecounter tc;
+ unsigned long ovfl_check_period;
u32 def_inc;
s16 slow_cmp_inc;
u32 slow_cmp_count;
@@ -39,6 +39,8 @@
#define TX_START_DELAY 0x40
#define TX_CLK_DELAY_100M 0x6
+#define TIMESYNC_SECONDS_BIT_MASK 0x0000ffffffffffff
+
static struct prueth_fw_offsets fw_offsets_v2_1;
static void icssm_prueth_set_fw_offsets(struct prueth *prueth)
@@ -637,13 +639,49 @@ irqreturn_t icssm_prueth_ptp_tx_irq_handle(int irq, void *dev)
return IRQ_HANDLED;
}
+/**
+ * icssm_iep_get_timestamp_cycles - IEP get timestamp
+ * @iep: icss_iep structure
+ * @mem: io memory address
+ *
+ * To convert the 10 byte timestamp from firmware
+ * i.e., nanoseconds part from 32-bit IEP counter(4 bytes)
+ * seconds part updated by firmware(rev FW_REV1_0) in SRAM
+ * (6 bytes) into 64-bit timestamp in ns
+ *
+ * Return: 64-bit converted timestamp
+ */
+u64 icssm_iep_get_timestamp_cycles(struct icss_iep *iep,
+ void __iomem *mem)
+{
+ u64 cycles, cycles_sec = 0;
+ u32 cycles_ns;
+
+ memcpy_fromio(&cycles_ns, mem, sizeof(cycles_ns));
+ memcpy_fromio(&cycles_sec, mem + 4, sizeof(cycles_sec));
+
+ /*To get the 6 bytes seconds part*/
+ cycles_sec = (cycles_sec & TIMESYNC_SECONDS_BIT_MASK);
+ cycles = cycles_ns + (cycles_sec * NSEC_PER_SEC);
+ cycles = timecounter_cyc2time(&iep->tc, cycles);
+
+ return cycles;
+}
+
static u64 icssm_prueth_ptp_ts_get(struct prueth_emac *emac, u32 ts_offs)
{
void __iomem *sram = emac->prueth->mem[PRUETH_MEM_SHARED_RAM].va;
u64 cycles;
- memcpy_fromio(&cycles, sram + ts_offs, sizeof(cycles));
- memset_io(sram + ts_offs, 0, sizeof(cycles));
+ if (emac->prueth->fw_data->fw_rev == FW_REV_V1_0) {
+ cycles = icssm_iep_get_timestamp_cycles(emac->prueth->iep,
+ sram + ts_offs);
+ /* 4 bytes of timestamp + 6 bytes of seconds counter */
+ memset_io(sram + ts_offs, 0, 10);
+ } else {
+ memcpy_fromio(&cycles, sram + ts_offs, sizeof(cycles));
+ memset_io(sram + ts_offs, 0, sizeof(cycles));
+ }
return cycles;
}
@@ -952,8 +990,14 @@ int icssm_emac_rx_packet(struct prueth_emac *emac, u16 *bd_rd_ptr,
if (pkt_info->timestamp) {
src_addr = (void *)PTR_ALIGN((uintptr_t)src_addr,
ICSS_BLOCK_SIZE);
- dst_addr = &ts;
- memcpy(dst_addr, src_addr, sizeof(ts));
+ if (emac->prueth->fw_data->fw_rev == FW_REV_V1_0) {
+ ts = icssm_iep_get_timestamp_cycles
+ (emac->prueth->iep,
+ (void __iomem *)src_addr);
+ } else {
+ dst_addr = &ts;
+ memcpy(dst_addr, src_addr, sizeof(ts));
+ }
}
if (!pkt_info->sv_frame) {
@@ -2173,6 +2217,9 @@ static int icssm_prueth_probe(struct platform_device *pdev)
goto netdev_exit;
}
+ if (prueth->fw_data->fw_rev == FW_REV_V1_0)
+ prueth->iep->sram = prueth->mem[PRUETH_MEM_SHARED_RAM].va;
+
/* Make rx interrupt pacing optional so that users can use ECAP for
* other use cases if needed
*/
@@ -2380,6 +2427,7 @@ static struct prueth_private_data am335x_prueth_pdata = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am335x-pru1-prueth-fw.elf",
},
+ .fw_rev = FW_REV_V1_0,
};
/* AM437x SoC-specific firmware data */
@@ -2393,6 +2441,7 @@ static struct prueth_private_data am437x_prueth_pdata = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am437x-pru1-prueth-fw.elf",
},
+ .fw_rev = FW_REV_V1_0,
};
/* AM57xx SoC-specific firmware data */
@@ -2406,6 +2455,7 @@ static struct prueth_private_data am57xx_prueth_pdata = {
.fw_name[PRUSS_ETHTYPE_EMAC] =
"ti-pruss/am57xx-pru1-prueth-fw.elf",
},
+ .fw_rev = FW_REV_V2_1,
};
static const struct of_device_id prueth_dt_match[] = {
@@ -297,6 +297,12 @@ enum prueth_mem {
PRUETH_MEM_MAX,
};
+/* PRU firmware revision*/
+enum fw_revision {
+ FW_REV_V1_0 = 0,
+ FW_REV_V2_1
+};
+
/* Firmware offsets/size information */
struct prueth_fw_offsets {
u32 index_array_offset;
@@ -337,6 +343,7 @@ enum pruss_device {
struct prueth_private_data {
enum pruss_device driver_data;
const struct prueth_firmware fw_pru[PRUSS_NUM_PRUS];
+ enum fw_revision fw_rev;
bool support_lre;
bool support_switch;
};
@@ -426,6 +433,9 @@ int icssm_emac_add_del_vid(struct prueth_emac *emac,
irqreturn_t icssm_prueth_ptp_tx_irq_handle(int irq, void *dev);
irqreturn_t icssm_prueth_ptp_tx_irq_work(int irq, void *dev);
+u64 icssm_iep_get_timestamp_cycles(struct icss_iep *iep,
+ void __iomem *mem);
+
void icssm_emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash);
void icssm_emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash);
u8 icssm_emac_get_mc_hash(u8 *mac, u8 *mask);