Re: [PATCH v4 3/5] net: ti: icss-iep: Add IEP driver
From: Md Danish Anwar
Date: Tue Aug 15 2023 - 06:47:29 EST
On 15/08/23 3:44 pm, Roger Quadros wrote:
>
>
> On 15/08/2023 13:07, Anwar, Md Danish wrote:
>> Hi Roger,
>>
>> On 8/15/2023 3:11 PM, Roger Quadros wrote:
>>> Hi Danish,
>>>
>>> On 14/08/2023 13:08, MD Danish Anwar wrote:
>>>> From: Roger Quadros <rogerq@xxxxxx>
>>>>
>>>> Add a driver for Industrial Ethernet Peripheral (IEP) block of PRUSS to
>>>> support timestamping of ethernet packets and thus support PTP and PPS
>>>> for PRU ethernet ports.
>>>>
>>>> Signed-off-by: Roger Quadros <rogerq@xxxxxx>
>>>> Signed-off-by: Lokesh Vutla <lokeshvutla@xxxxxx>
>>>> Signed-off-by: Murali Karicheri <m-karicheri2@xxxxxx>
>>>> Signed-off-by: Vignesh Raghavendra <vigneshr@xxxxxx>
>>>> Signed-off-by: MD Danish Anwar <danishanwar@xxxxxx>
>>>> ---
>>>> drivers/net/ethernet/ti/Kconfig | 11 +
>>>> drivers/net/ethernet/ti/Makefile | 1 +
>>>> drivers/net/ethernet/ti/icssg/icss_iep.c | 921 +++++++++++++++++++++++
>>>> drivers/net/ethernet/ti/icssg/icss_iep.h | 38 +
>>>> 4 files changed, 971 insertions(+)
>>>> create mode 100644 drivers/net/ethernet/ti/icssg/icss_iep.c
>>>> create mode 100644 drivers/net/ethernet/ti/icssg/icss_iep.h
>>>>
>>>> diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig
>>>> index 63e510b6860f..1af5a90720ec 100644
>>>> --- a/drivers/net/ethernet/ti/Kconfig
>>>> +++ b/drivers/net/ethernet/ti/Kconfig
>>>> @@ -196,4 +196,15 @@ config TI_ICSSG_PRUETH
>>>> to support the Ethernet operation. Currently, it supports Ethernet
>>>> with 1G and 100M link speed.
>>>> +config TI_ICSS_IEP
>>>> + tristate "TI PRU ICSS IEP driver"
>>>> + depends on TI_PRUSS
>>>> + default TI_PRUSS
>>>> + help
>>>> + This driver enables support for the PRU-ICSS Industrial Ethernet
>>>> + Peripheral within a PRU-ICSS subsystem present on various TI SoCs.
>>>> +
>>>> + To compile this driver as a module, choose M here. The module
>>>> + will be called icss_iep.
>>>> +
>>>> endif # NET_VENDOR_TI
>>>> diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile
>>>> index 9176d79c36e1..34fd7a716ba6 100644
>>>> --- a/drivers/net/ethernet/ti/Makefile
>>>> +++ b/drivers/net/ethernet/ti/Makefile
>>>> @@ -38,3 +38,4 @@ icssg-prueth-y := k3-cppi-desc-pool.o \
>>>> icssg/icssg_mii_cfg.o \
>>>> icssg/icssg_stats.o \
>>>> icssg/icssg_ethtool.o
>>>> +obj-$(CONFIG_TI_ICSS_IEP) += icssg/icss_iep.o
>>>> diff --git a/drivers/net/ethernet/ti/icssg/icss_iep.c b/drivers/net/ethernet/ti/icssg/icss_iep.c
>>>> new file mode 100644
>>>> index 000000000000..d123b8ba3f31
>>>> --- /dev/null
>>>> +++ b/drivers/net/ethernet/ti/icssg/icss_iep.c
>>>> @@ -0,0 +1,921 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +
>>>> +/* Texas Instruments ICSSG Industrial Ethernet Peripheral (IEP) Driver
>>>> + *
>>>> + * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com
>>>> + *
>>>> + */
>>>> +
>>>> +#include <linux/bitops.h>
>>>> +#include <linux/clk.h>
>>>> +#include <linux/err.h>
>>>> +#include <linux/io.h>
>>>> +#include <linux/module.h>
>>>> +#include <linux/of.h>
>>>> +#include <linux/of_platform.h>
>>>> +#include <linux/platform_device.h>
>>>> +#include <linux/timekeeping.h>
>>>> +#include <linux/interrupt.h>
>>>> +#include <linux/of_irq.h>
>>>> +
>>>> +#include "icss_iep.h"
>>>> +
>>>> +#define IEP_MAX_DEF_INC 0xf
>>>> +#define IEP_MAX_COMPEN_INC 0xfff
>>>> +#define IEP_MAX_COMPEN_COUNT 0xffffff
>>>> +
>>>> +#define IEP_GLOBAL_CFG_CNT_ENABLE BIT(0)
>>>> +#define IEP_GLOBAL_CFG_DEFAULT_INC_MASK GENMASK(7, 4)
>>>> +#define IEP_GLOBAL_CFG_DEFAULT_INC_SHIFT 4
>>>> +#define IEP_GLOBAL_CFG_COMPEN_INC_MASK GENMASK(19, 8)
>>>> +#define IEP_GLOBAL_CFG_COMPEN_INC_SHIFT 8
>>>> +
>>>> +#define IEP_GLOBAL_STATUS_CNT_OVF BIT(0)
>>>> +
>>>> +#define IEP_CMP_CFG_SHADOW_EN BIT(17)
>>>> +#define IEP_CMP_CFG_CMP0_RST_CNT_EN BIT(0)
>>>> +#define IEP_CMP_CFG_CMP_EN(cmp) (GENMASK(16, 1) & (1 << ((cmp) + 1)))
>>>> +
>>>> +#define IEP_CMP_STATUS(cmp) (1 << (cmp))
>>>> +
>>>> +#define IEP_SYNC_CTRL_SYNC_EN BIT(0)
>>>> +#define IEP_SYNC_CTRL_SYNC_N_EN(n) (GENMASK(2, 1) & (BIT(1) << (n)))
>>>> +
>>>> +#define IEP_MIN_CMP 0
>>>> +#define IEP_MAX_CMP 15
>>>> +
>>>> +#define ICSS_IEP_64BIT_COUNTER_SUPPORT BIT(0)
>>>> +#define ICSS_IEP_SLOW_COMPEN_REG_SUPPORT BIT(1)
>>>> +#define ICSS_IEP_SHADOW_MODE_SUPPORT BIT(2)
>>>> +
>>>> +#define LATCH_INDEX(ts_index) ((ts_index) + 6)
>>>> +#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)
>>>> +
>>>> +enum {
>>>> + ICSS_IEP_GLOBAL_CFG_REG,
>>>> + ICSS_IEP_GLOBAL_STATUS_REG,
>>>> + ICSS_IEP_COMPEN_REG,
>>>> + ICSS_IEP_SLOW_COMPEN_REG,
>>>> + ICSS_IEP_COUNT_REG0,
>>>> + ICSS_IEP_COUNT_REG1,
>>>> + ICSS_IEP_CAPTURE_CFG_REG,
>>>> + ICSS_IEP_CAPTURE_STAT_REG,
>>>> +
>>>> + ICSS_IEP_CAP6_RISE_REG0,
>>>> + ICSS_IEP_CAP6_RISE_REG1,
>>>> +
>>>> + ICSS_IEP_CAP7_RISE_REG0,
>>>> + ICSS_IEP_CAP7_RISE_REG1,
>>>> +
>>>> + ICSS_IEP_CMP_CFG_REG,
>>>> + ICSS_IEP_CMP_STAT_REG,
>>>> + ICSS_IEP_CMP0_REG0,
>>>> + ICSS_IEP_CMP0_REG1,
>>>> + ICSS_IEP_CMP1_REG0,
>>>> + ICSS_IEP_CMP1_REG1,
>>>> +
>>>> + ICSS_IEP_CMP8_REG0,
>>>> + ICSS_IEP_CMP8_REG1,
>>>> + ICSS_IEP_SYNC_CTRL_REG,
>>>> + ICSS_IEP_SYNC0_STAT_REG,
>>>> + ICSS_IEP_SYNC1_STAT_REG,
>>>> + ICSS_IEP_SYNC_PWIDTH_REG,
>>>> + ICSS_IEP_SYNC0_PERIOD_REG,
>>>> + ICSS_IEP_SYNC1_DELAY_REG,
>>>> + ICSS_IEP_SYNC_START_REG,
>>>> + ICSS_IEP_MAX_REGS,
>>>> +};
>>>> +
>>>> +/**
>>>> + * struct icss_iep_plat_data - Plat data to handle SoC variants
>>>> + * @config: Regmap configuration data
>>>> + * @reg_offs: register offsets to capture offset differences across SoCs
>>>> + * @flags: Flags to represent IEP properties
>>>> + */
>>>> +struct icss_iep_plat_data {
>>>> + struct regmap_config *config;
>>>> + u32 reg_offs[ICSS_IEP_MAX_REGS];
>>>> + u32 flags;
>>>> +};
>>>> +
>>>> +struct icss_iep {
>>>> + struct device *dev;
>>>> + void __iomem *base;
>>>> + const struct icss_iep_plat_data *plat_data;
>>>> + struct regmap *map;
>>>> + struct device_node *client_np;
>>>> + unsigned long refclk_freq;
>>>> + int clk_tick_time; /* one refclk tick time in ns */
>>>> + struct ptp_clock_info ptp_info;
>>>> + struct ptp_clock *ptp_clock;
>>>> + struct mutex ptp_clk_mutex; /* PHC access serializer */
>>>> + spinlock_t irq_lock; /* CMP IRQ vs icss_iep_ptp_enable access */
>>>> + u32 def_inc;
>>>> + s16 slow_cmp_inc;
>>>> + u32 slow_cmp_count;
>>>> + const struct icss_iep_clockops *ops;
>>>> + void *clockops_data;
>>>> + u32 cycle_time_ns;
>>>> + u32 perout_enabled;
>>>> + bool pps_enabled;
>>>> + int cap_cmp_irq;
>>>> + u64 period;
>>>> + u32 latch_enable;
>>>> +};
>>>> +
>>>
>>> Where is the comment you agreed to add on why we are using readl/writel
>>> instead of regmap in certain areas?
>>>
>>
>> We agreed to add comment on why we are using readl/writel instead of regmap in the documentation of the readl / writel helper APIs (icss_iep_readl / writel ). But Andrew asked me to drop those helper APIs, so I dropped them. Now no helper APIs are there and we are directly using readl / writel in total 8 places. Previously with the helper APIs, there was a way to document the use of readl / writel before the declaration of heper APIs. Now with no helper APIs, I couldn't find a good place to add this documentation as all 8 instances using readl / writel seems similar. So I dropped it.
>
> Please limit your reply to around 80 columns wide.
> https://people.kernel.org/tglx/notes-about-netiquette
>
Sorry about that. I replied from my laptop instead of my PC as I am not in
office today (India holiday). I might have not added line wrap settings in my
laptop.
> You don't have to put the comment everywhere you use readl/writel
> but you need to put it at least once hopefully just before icss_iep_gettime()
> so future readers of this code know the reasoning why we are not
> using regmap API throughout.
>
Sure, then. I will add the comment before icss_iep_gettime() API in next revision.
>>
>>>> +/**
>>>> + * icss_iep_get_count_hi() - Get the upper 32 bit IEP counter
>>>> + * @iep: Pointer to structure representing IEP.
>>>> + *
>>>> + * Return: upper 32 bit IEP counter
>>>> + */
>>>> +int icss_iep_get_count_hi(struct icss_iep *iep)
>>>> +{
>>>> + u32 val = 0;
>>>> +
>>>> + if (iep && (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT))
>>>> + val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);
>>>> +
>>>> + return val;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(icss_iep_get_count_hi);
>>>> +
>>>> +/**
>>>> + * icss_iep_get_count_low() - Get the lower 32 bit IEP counter
>>>> + * @iep: Pointer to structure representing IEP.
>>>> + *
>>>> + * Return: lower 32 bit IEP counter
>>>> + */
>>>> +int icss_iep_get_count_low(struct icss_iep *iep)
>>>> +{
>>>> + u32 val = 0;
>>>> +
>>>> + if (iep)
>>>> + val = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);
>>>> +
>>>> + return val;
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(icss_iep_get_count_low);
>>>> +
>>>> +/**
>>>> + * icss_iep_get_ptp_clock_idx() - Get PTP clock index using IEP driver
>>>> + * @iep: Pointer to structure representing IEP.
>>>> + *
>>>> + * Return: PTP clock index, -1 if not registered
>>>> + */
>>>> +int icss_iep_get_ptp_clock_idx(struct icss_iep *iep)
>>>> +{
>>>> + if (!iep || !iep->ptp_clock)
>>>> + return -1;
>>>> + return ptp_clock_index(iep->ptp_clock);
>>>> +}
>>>> +EXPORT_SYMBOL_GPL(icss_iep_get_ptp_clock_idx);
>>>> +
>>>> +static void icss_iep_set_counter(struct icss_iep *iep, u64 ns)
>>>> +{
>>>> + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
>>>> + writel(upper_32_bits(ns), iep->base +
>>>> + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);
>>>> + writel(upper_32_bits(ns), iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);
>>>> +}
>>>> +
>>>> +static void icss_iep_update_to_next_boundary(struct icss_iep *iep, u64 start_ns);
>>>> +
>>>> +static void icss_iep_settime(struct icss_iep *iep, u64 ns)
>>>> +{
>>>> + unsigned long flags;
>>>> +
>>>> + if (iep->ops && iep->ops->settime) {
>>>> + iep->ops->settime(iep->clockops_data, ns);
>>>> + return;
>>>> + }
>>>> +
>>>> + spin_lock_irqsave(&iep->irq_lock, flags);
>>>> + if (iep->pps_enabled || iep->perout_enabled)
>>>> + writel(0, iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]);
>>>> +
>>>> + icss_iep_set_counter(iep, ns);
>>>> +
>>>> + if (iep->pps_enabled || iep->perout_enabled) {
>>>> + icss_iep_update_to_next_boundary(iep, ns);
>>>> + writel(IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN,
>>>> + iep->base + iep->plat_data->reg_offs[ICSS_IEP_SYNC_CTRL_REG]);
>>>> + }
>>>> + spin_unlock_irqrestore(&iep->irq_lock, flags);
>>>> +}
>>>> +
>>>> +static u64 icss_iep_gettime(struct icss_iep *iep,
>>>> + struct ptp_system_timestamp *sts)
>>>> +{
>>>> + u32 ts_hi = 0, ts_lo;
>>>> + unsigned long flags;
>>>> +
>>>> + if (iep->ops && iep->ops->gettime)
>>>> + return iep->ops->gettime(iep->clockops_data, sts);
>>>> +
>>>> + /* use local_irq_x() to make it work for both RT/non-RT */
>>>> + local_irq_save(flags);
>>>> +
>>>> + /* no need to play with hi-lo, hi is latched when lo is read */
>>>> + ptp_read_system_prets(sts);
>>>> + ts_lo = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG0]);
>>>> + ptp_read_system_postts(sts);
>>>> + if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
>>>> + ts_hi = readl(iep->base + iep->plat_data->reg_offs[ICSS_IEP_COUNT_REG1]);
>>>> +
>>>> + local_irq_restore(flags);
>>>> +
>>>> + return (u64)ts_lo | (u64)ts_hi << 32;
>>>> +}
>>>> +
>>>> +static void icss_iep_enable(struct icss_iep *iep)
>>>> +{
>>>> + regmap_update_bits(iep->map, ICSS_IEP_GLOBAL_CFG_REG,
>>>> + IEP_GLOBAL_CFG_CNT_ENABLE,
>>>> + IEP_GLOBAL_CFG_CNT_ENABLE);
>>>> +}
>>>> +
>>> <snip>
>>>
>>
>
--
Thanks and Regards,
Danish.