[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CA+h21hruexC=q6BSW_1DSJeVuTRCBQ1ZMjMQOWLm+o7qbvfhkw@mail.gmail.com>
Date: Fri, 20 Mar 2020 18:37:06 +0200
From: Vladimir Oltean <olteanv@...il.com>
To: Yangbo Lu <yangbo.lu@....com>
Cc: lkml <linux-kernel@...r.kernel.org>,
netdev <netdev@...r.kernel.org>,
"David S . Miller" <davem@...emloft.net>,
Richard Cochran <richardcochran@...il.com>,
Vladimir Oltean <vladimir.oltean@....com>,
Claudiu Manoil <claudiu.manoil@....com>,
Andrew Lunn <andrew@...n.ch>,
Vivien Didelot <vivien.didelot@...il.com>,
Florian Fainelli <f.fainelli@...il.com>,
Alexandre Belloni <alexandre.belloni@...tlin.com>,
Microchip Linux Driver Support <UNGLinuxDriver@...rochip.com>
Subject: Re: [PATCH 1/6] ptp: move ocelot ptp clock code out of Ethernet driver
I hate to be that guy who reports build error before the Kbuild robot
does, but here goes.
On Fri, 20 Mar 2020 at 12:41, Yangbo Lu <yangbo.lu@....com> wrote:
>
> The Ocelot PTP clock driver had been embedded into ocelot.c driver.
> It had supported basic gettime64/settime64/adjtime/adjfine functions
> by now which were used by both Ocelot switch and Felix switch.
>
> This patch is to move current ptp clock code out of ocelot.c driver
> maintaining as a single ptp_ocelot.c driver.
> For futher new features implementation, the common code could be put
> in ptp_ocelot.c driver and the switch specific code should be in
> specific switch driver. The interrupt implementation in SoC is different
> between Ocelot and Felix.
>
> Signed-off-by: Yangbo Lu <yangbo.lu@....com>
> ---
> drivers/net/dsa/ocelot/felix.c | 3 +-
> drivers/net/ethernet/mscc/ocelot.c | 201 +------------------
> drivers/net/ethernet/mscc/ocelot.h | 3 +-
> drivers/net/ethernet/mscc/ocelot_board.c | 1 +
> drivers/ptp/Kconfig | 10 +
> drivers/ptp/Makefile | 1 +
> drivers/ptp/ptp_ocelot.c | 217 +++++++++++++++++++++
> include/soc/mscc/ocelot.h | 1 -
> .../net/ethernet => include/soc}/mscc/ocelot_ptp.h | 1 +
> include/soc/mscc/ptp_ocelot.h | 34 ++++
> 10 files changed, 271 insertions(+), 201 deletions(-)
> create mode 100644 drivers/ptp/ptp_ocelot.c
> rename {drivers/net/ethernet => include/soc}/mscc/ocelot_ptp.h (97%)
> create mode 100644 include/soc/mscc/ptp_ocelot.h
>
> diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
> index 6954638..9f9efb9 100644
> --- a/drivers/net/dsa/ocelot/felix.c
> +++ b/drivers/net/dsa/ocelot/felix.c
> @@ -8,6 +8,7 @@
> #include <soc/mscc/ocelot_dev.h>
> #include <soc/mscc/ocelot_ana.h>
> #include <soc/mscc/ocelot.h>
> +#include <soc/mscc/ptp_ocelot.h>
> #include <linux/packing.h>
> #include <linux/module.h>
> #include <linux/of_net.h>
> @@ -576,7 +577,7 @@ static bool felix_rxtstamp(struct dsa_switch *ds, int port,
> struct ocelot *ocelot = ds->priv;
> u8 *extraction = skb->data - ETH_HLEN - OCELOT_TAG_LEN;
> u32 tstamp_lo, tstamp_hi;
> - struct timespec64 ts;
> + struct timespec64 ts = {0, 0};
> u64 tstamp, val;
>
> ocelot_ptp_gettime64(&ocelot->ptp_info, &ts);
> diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
> index dc0e273..b342bbd 100644
> --- a/drivers/net/ethernet/mscc/ocelot.c
> +++ b/drivers/net/ethernet/mscc/ocelot.c
> @@ -21,6 +21,7 @@
> #include <net/netevent.h>
> #include <net/rtnetlink.h>
> #include <net/switchdev.h>
> +#include <soc/mscc/ptp_ocelot.h>
>
> #include "ocelot.h"
> #include "ocelot_ace.h"
> @@ -1989,200 +1990,6 @@ struct notifier_block ocelot_switchdev_blocking_nb __read_mostly = {
> };
> EXPORT_SYMBOL(ocelot_switchdev_blocking_nb);
>
> -int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
> -{
> - struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
> - unsigned long flags;
> - time64_t s;
> - u32 val;
> - s64 ns;
> -
> - spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
> -
> - val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> - val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
> - val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
> - ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> -
> - s = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN) & 0xffff;
> - s <<= 32;
> - s += ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
> - ns = ocelot_read_rix(ocelot, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
> -
> - spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> -
> - /* Deal with negative values */
> - if (ns >= 0x3ffffff0 && ns <= 0x3fffffff) {
> - s--;
> - ns &= 0xf;
> - ns += 999999984;
> - }
> -
> - set_normalized_timespec64(ts, s, ns);
> - return 0;
> -}
> -EXPORT_SYMBOL(ocelot_ptp_gettime64);
> -
> -static int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
> - const struct timespec64 *ts)
> -{
> - struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
> - unsigned long flags;
> - u32 val;
> -
> - spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
> -
> - val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> - val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
> - val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
> -
> - ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> -
> - ocelot_write_rix(ocelot, lower_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_LSB,
> - TOD_ACC_PIN);
> - ocelot_write_rix(ocelot, upper_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_MSB,
> - TOD_ACC_PIN);
> - ocelot_write_rix(ocelot, ts->tv_nsec, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
> -
> - val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> - val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
> - val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_LOAD);
> -
> - ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> -
> - spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> - return 0;
> -}
> -
> -static int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
> -{
> - if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
> - struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
> - unsigned long flags;
> - u32 val;
> -
> - spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
> -
> - val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> - val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
> - val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
> -
> - ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> -
> - ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
> - ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN);
> - ocelot_write_rix(ocelot, delta, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
> -
> - val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> - val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
> - val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_DELTA);
> -
> - ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> -
> - spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> - } else {
> - /* Fall back using ocelot_ptp_settime64 which is not exact. */
> - struct timespec64 ts;
> - u64 now;
> -
> - ocelot_ptp_gettime64(ptp, &ts);
> -
> - now = ktime_to_ns(timespec64_to_ktime(ts));
> - ts = ns_to_timespec64(now + delta);
> -
> - ocelot_ptp_settime64(ptp, &ts);
> - }
> - return 0;
> -}
> -
> -static int ocelot_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
> -{
> - struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
> - u32 unit = 0, direction = 0;
> - unsigned long flags;
> - u64 adj = 0;
> -
> - spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
> -
> - if (!scaled_ppm)
> - goto disable_adj;
> -
> - if (scaled_ppm < 0) {
> - direction = PTP_CFG_CLK_ADJ_CFG_DIR;
> - scaled_ppm = -scaled_ppm;
> - }
> -
> - adj = PSEC_PER_SEC << 16;
> - do_div(adj, scaled_ppm);
> - do_div(adj, 1000);
> -
> - /* If the adjustment value is too large, use ns instead */
> - if (adj >= (1L << 30)) {
> - unit = PTP_CFG_CLK_ADJ_FREQ_NS;
> - do_div(adj, 1000);
> - }
> -
> - /* Still too big */
> - if (adj >= (1L << 30))
> - goto disable_adj;
> -
> - ocelot_write(ocelot, unit | adj, PTP_CLK_CFG_ADJ_FREQ);
> - ocelot_write(ocelot, PTP_CFG_CLK_ADJ_CFG_ENA | direction,
> - PTP_CLK_CFG_ADJ_CFG);
> -
> - spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> - return 0;
> -
> -disable_adj:
> - ocelot_write(ocelot, 0, PTP_CLK_CFG_ADJ_CFG);
> -
> - spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> - return 0;
> -}
> -
> -static struct ptp_clock_info ocelot_ptp_clock_info = {
> - .owner = THIS_MODULE,
> - .name = "ocelot ptp",
> - .max_adj = 0x7fffffff,
> - .n_alarm = 0,
> - .n_ext_ts = 0,
> - .n_per_out = 0,
> - .n_pins = 0,
> - .pps = 0,
> - .gettime64 = ocelot_ptp_gettime64,
> - .settime64 = ocelot_ptp_settime64,
> - .adjtime = ocelot_ptp_adjtime,
> - .adjfine = ocelot_ptp_adjfine,
> -};
> -
> -static int ocelot_init_timestamp(struct ocelot *ocelot)
> -{
> - struct ptp_clock *ptp_clock;
> -
> - ocelot->ptp_info = ocelot_ptp_clock_info;
> - ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev);
> - if (IS_ERR(ptp_clock))
> - return PTR_ERR(ptp_clock);
> - /* Check if PHC support is missing at the configuration level */
> - if (!ptp_clock)
> - return 0;
> -
> - ocelot->ptp_clock = ptp_clock;
> -
> - ocelot_write(ocelot, SYS_PTP_CFG_PTP_STAMP_WID(30), SYS_PTP_CFG);
> - ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_LOW);
> - ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_HIGH);
> -
> - ocelot_write(ocelot, PTP_CFG_MISC_PTP_EN, PTP_CFG_MISC);
> -
> - /* There is no device reconfiguration, PTP Rx stamping is always
> - * enabled.
> - */
> - ocelot->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
> -
> - return 0;
> -}
> -
> /* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu.
> * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG.
> */
> @@ -2507,7 +2314,8 @@ int ocelot_init(struct ocelot *ocelot)
> ret = ocelot_init_timestamp(ocelot);
> if (ret) {
> dev_err(ocelot->dev,
> - "Timestamp initialization failed\n");
> + "Timestamp not enabled or initialization failed\n");
> + ocelot->ptp = 0;
> return ret;
> }
> }
> @@ -2524,8 +2332,7 @@ void ocelot_deinit(struct ocelot *ocelot)
> cancel_delayed_work(&ocelot->stats_work);
> destroy_workqueue(ocelot->stats_queue);
> mutex_destroy(&ocelot->stats_lock);
> - if (ocelot->ptp_clock)
> - ptp_clock_unregister(ocelot->ptp_clock);
> + ocelot_deinit_timestamp(ocelot);
>
> for (i = 0; i < ocelot->num_phys_ports; i++) {
> port = ocelot->ports[i];
> diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h
> index e34ef83..5aa2e45 100644
> --- a/drivers/net/ethernet/mscc/ocelot.h
> +++ b/drivers/net/ethernet/mscc/ocelot.h
> @@ -15,18 +15,17 @@
> #include <linux/phy.h>
> #include <linux/phy/phy.h>
> #include <linux/platform_device.h>
> -#include <linux/ptp_clock_kernel.h>
> #include <linux/regmap.h>
>
> #include <soc/mscc/ocelot_qsys.h>
> #include <soc/mscc/ocelot_sys.h>
> #include <soc/mscc/ocelot_dev.h>
> #include <soc/mscc/ocelot_ana.h>
> +#include <soc/mscc/ocelot_ptp.h>
> #include <soc/mscc/ocelot.h>
> #include "ocelot_rew.h"
> #include "ocelot_qs.h"
> #include "ocelot_tc.h"
> -#include "ocelot_ptp.h"
>
> #define OCELOT_BUFFER_CELL_SZ 60
>
> diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c
> index 0ac9fbf7..7e59cee 100644
> --- a/drivers/net/ethernet/mscc/ocelot_board.c
> +++ b/drivers/net/ethernet/mscc/ocelot_board.c
> @@ -14,6 +14,7 @@
> #include <linux/skbuff.h>
> #include <net/switchdev.h>
>
> +#include <soc/mscc/ptp_ocelot.h>
> #include <soc/mscc/ocelot_vcap.h>
> #include "ocelot.h"
>
> diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
> index 86400c7..ac08e9c 100644
> --- a/drivers/ptp/Kconfig
> +++ b/drivers/ptp/Kconfig
> @@ -151,4 +151,14 @@ config PTP_1588_CLOCK_VMW
> To compile this driver as a module, choose M here: the module
> will be called ptp_vmw.
>
> +config PTP_1588_CLOCK_OCELOT
> + bool "Microsemi Ocelot as PTP clock"
Why bool and not tristate? Compilation breaks when
MSCC_OCELOT_SWITCH=m because it forces PTP_1588_CLOCK_OCELOT=y.
drivers/ptp/ptp_ocelot.o: In function `ocelot_ptp_settime64':
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:56: undefined reference
to `__ocelot_read_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:60: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:62: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:64: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:66: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:68: undefined reference
to `__ocelot_read_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:72: undefined reference
to `__ocelot_write_ix'
drivers/ptp/ptp_ocelot.o: In function `ocelot_ptp_adjfine':
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:161: undefined
reference to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:153: undefined
reference to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:154: undefined
reference to `__ocelot_write_ix'
drivers/ptp/ptp_ocelot.o: In function `ocelot_ptp_gettime64':
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:23: undefined reference
to `__ocelot_read_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:26: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:28: undefined reference
to `__ocelot_read_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:30: undefined reference
to `__ocelot_read_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:31: undefined reference
to `__ocelot_read_ix'
drivers/ptp/ptp_ocelot.o: In function `ocelot_ptp_enable':
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:231: undefined
reference to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:232: undefined
reference to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:235: undefined
reference to `__ocelot_write_ix'
drivers/ptp/ptp_ocelot.o: In function `ocelot_init_timestamp':
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:289: undefined
reference to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:290: undefined
reference to `__ocelot_write_ix'
drivers/ptp/ptp_ocelot.o:/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:291:
more undefined references to `__ocelot_write_ix' follow
drivers/ptp/ptp_ocelot.o: In function `ocelot_ptp_adjtime':
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:88: undefined reference
to `__ocelot_read_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:93: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:95: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:96: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:97: undefined reference
to `__ocelot_write_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:99: undefined reference
to `__ocelot_read_ix'
/opt/ls1028ardb-linux/drivers/ptp/ptp_ocelot.c:104: undefined
reference to `__ocelot_write_ix'
> + depends on MSCC_OCELOT_SWITCH || COMPILE_TEST
> + depends on PTP_1588_CLOCK
> + default y
> + help
> + This driver adds support for using Microsemi Ocelot as a PTP
> + clock. This clock is only useful if your PTP programs are
> + getting hardware time stamps on the PTP Ethernet packets using
> + the SO_TIMESTAMPING API.
> endmenu
> diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
> index 7aff75f..a0229b3 100644
> --- a/drivers/ptp/Makefile
> +++ b/drivers/ptp/Makefile
> @@ -15,3 +15,4 @@ ptp-qoriq-$(CONFIG_DEBUG_FS) += ptp_qoriq_debugfs.o
> obj-$(CONFIG_PTP_1588_CLOCK_IDTCM) += ptp_clockmatrix.o
> obj-$(CONFIG_PTP_1588_CLOCK_IDT82P33) += ptp_idt82p33.o
> obj-$(CONFIG_PTP_1588_CLOCK_VMW) += ptp_vmw.o
> +obj-$(CONFIG_PTP_1588_CLOCK_OCELOT) += ptp_ocelot.o
> diff --git a/drivers/ptp/ptp_ocelot.c b/drivers/ptp/ptp_ocelot.c
> new file mode 100644
> index 0000000..59420a7
> --- /dev/null
> +++ b/drivers/ptp/ptp_ocelot.c
> @@ -0,0 +1,217 @@
> +// SPDX-License-Identifier: (GPL-2.0 OR MIT)
> +/*
> + * Microsemi Ocelot PTP clock driver
> + *
> + * Copyright (c) 2017 Microsemi Corporation
> + * Copyright 2020 NXP
> + */
> +#include <soc/mscc/ocelot_ptp.h>
> +#include <soc/mscc/ocelot_sys.h>
> +#include <soc/mscc/ocelot.h>
> +#include <soc/mscc/ptp_ocelot.h>
> +
> +int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts)
> +{
> + struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
> + unsigned long flags;
> + time64_t s;
> + u32 val;
> + s64 ns;
> +
> + spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
> +
> + val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> + val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
> + val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_SAVE);
> + ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> +
> + s = ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN) & 0xffff;
> + s <<= 32;
> + s += ocelot_read_rix(ocelot, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
> + ns = ocelot_read_rix(ocelot, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
> +
> + spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> +
> + /* Deal with negative values */
> + if (ns >= 0x3ffffff0 && ns <= 0x3fffffff) {
> + s--;
> + ns &= 0xf;
> + ns += 999999984;
> + }
> +
> + set_normalized_timespec64(ts, s, ns);
> + return 0;
> +}
> +EXPORT_SYMBOL(ocelot_ptp_gettime64);
> +
> +static int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
> + const struct timespec64 *ts)
> +{
> + struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
> + unsigned long flags;
> + u32 val;
> +
> + spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
> +
> + val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> + val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
> + val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
> +
> + ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> +
> + ocelot_write_rix(ocelot, lower_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_LSB,
> + TOD_ACC_PIN);
> + ocelot_write_rix(ocelot, upper_32_bits(ts->tv_sec), PTP_PIN_TOD_SEC_MSB,
> + TOD_ACC_PIN);
> + ocelot_write_rix(ocelot, ts->tv_nsec, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
> +
> + val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> + val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK | PTP_PIN_CFG_DOM);
> + val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_LOAD);
> +
> + ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> +
> + spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> + return 0;
> +}
> +
> +static int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
> +{
> + if (delta > -(NSEC_PER_SEC / 2) && delta < (NSEC_PER_SEC / 2)) {
> + struct ocelot *ocelot = container_of(ptp, struct ocelot,
> + ptp_info);
> + unsigned long flags;
> + u32 val;
> +
> + spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
> +
> + val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> + val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK |
> + PTP_PIN_CFG_DOM);
> + val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_IDLE);
> +
> + ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> +
> + ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_LSB, TOD_ACC_PIN);
> + ocelot_write_rix(ocelot, 0, PTP_PIN_TOD_SEC_MSB, TOD_ACC_PIN);
> + ocelot_write_rix(ocelot, delta, PTP_PIN_TOD_NSEC, TOD_ACC_PIN);
> +
> + val = ocelot_read_rix(ocelot, PTP_PIN_CFG, TOD_ACC_PIN);
> + val &= ~(PTP_PIN_CFG_SYNC | PTP_PIN_CFG_ACTION_MASK |
> + PTP_PIN_CFG_DOM);
> + val |= PTP_PIN_CFG_ACTION(PTP_PIN_ACTION_DELTA);
> +
> + ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
> +
> + spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> + } else {
> + /* Fall back using ocelot_ptp_settime64 which is not exact. */
> + struct timespec64 ts;
> + u64 now;
> +
> + ocelot_ptp_gettime64(ptp, &ts);
> +
> + now = ktime_to_ns(timespec64_to_ktime(ts));
> + ts = ns_to_timespec64(now + delta);
> +
> + ocelot_ptp_settime64(ptp, &ts);
> + }
> + return 0;
> +}
> +
> +static int ocelot_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
> +{
> + struct ocelot *ocelot = container_of(ptp, struct ocelot, ptp_info);
> + u32 unit = 0, direction = 0;
> + unsigned long flags;
> + u64 adj = 0;
> +
> + spin_lock_irqsave(&ocelot->ptp_clock_lock, flags);
> +
> + if (!scaled_ppm)
> + goto disable_adj;
> +
> + if (scaled_ppm < 0) {
> + direction = PTP_CFG_CLK_ADJ_CFG_DIR;
> + scaled_ppm = -scaled_ppm;
> + }
> +
> + adj = PSEC_PER_SEC << 16;
> + do_div(adj, scaled_ppm);
> + do_div(adj, 1000);
> +
> + /* If the adjustment value is too large, use ns instead */
> + if (adj >= (1L << 30)) {
> + unit = PTP_CFG_CLK_ADJ_FREQ_NS;
> + do_div(adj, 1000);
> + }
> +
> + /* Still too big */
> + if (adj >= (1L << 30))
> + goto disable_adj;
> +
> + ocelot_write(ocelot, unit | adj, PTP_CLK_CFG_ADJ_FREQ);
> + ocelot_write(ocelot, PTP_CFG_CLK_ADJ_CFG_ENA | direction,
> + PTP_CLK_CFG_ADJ_CFG);
> +
> + spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> + return 0;
> +
> +disable_adj:
> + ocelot_write(ocelot, 0, PTP_CLK_CFG_ADJ_CFG);
> +
> + spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
> + return 0;
> +}
> +
> +static struct ptp_clock_info ocelot_ptp_clock_info = {
> + .owner = THIS_MODULE,
> + .name = "ocelot ptp",
> + .max_adj = 0x7fffffff,
> + .n_alarm = 0,
> + .n_ext_ts = 0,
> + .n_per_out = 0,
> + .n_pins = 0,
> + .pps = 0,
> + .gettime64 = ocelot_ptp_gettime64,
> + .settime64 = ocelot_ptp_settime64,
> + .adjtime = ocelot_ptp_adjtime,
> + .adjfine = ocelot_ptp_adjfine,
> +};
> +
> +int ocelot_init_timestamp(struct ocelot *ocelot)
> +{
> + struct ptp_clock *ptp_clock;
> +
> + ocelot->ptp_info = ocelot_ptp_clock_info;
> + ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev);
> + if (IS_ERR(ptp_clock))
> + return PTR_ERR(ptp_clock);
> + /* Check if PHC support is missing at the configuration level */
> + if (!ptp_clock)
> + return 0;
> +
> + ocelot->ptp_clock = ptp_clock;
> +
> + ocelot_write(ocelot, SYS_PTP_CFG_PTP_STAMP_WID(30), SYS_PTP_CFG);
> + ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_LOW);
> + ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_HIGH);
> +
> + ocelot_write(ocelot, PTP_CFG_MISC_PTP_EN, PTP_CFG_MISC);
> +
> + /* There is no device reconfiguration, PTP Rx stamping is always
> + * enabled.
> + */
> + ocelot->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(ocelot_init_timestamp);
> +
> +int ocelot_deinit_timestamp(struct ocelot *ocelot)
> +{
> + if (ocelot->ptp_clock)
> + ptp_clock_unregister(ocelot->ptp_clock);
> + return 0;
> +}
> +EXPORT_SYMBOL(ocelot_deinit_timestamp);
> diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
> index 007b584..d9bad70 100644
> --- a/include/soc/mscc/ocelot.h
> +++ b/include/soc/mscc/ocelot.h
> @@ -607,7 +607,6 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
> int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid);
> int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr);
> int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr);
> -int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts);
> int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port,
> struct sk_buff *skb);
> void ocelot_get_txtstamp(struct ocelot *ocelot);
> diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.h b/include/soc/mscc/ocelot_ptp.h
> similarity index 97%
> rename from drivers/net/ethernet/mscc/ocelot_ptp.h
> rename to include/soc/mscc/ocelot_ptp.h
> index 9ede14a..2dd27f0 100644
> --- a/drivers/net/ethernet/mscc/ocelot_ptp.h
> +++ b/include/soc/mscc/ocelot_ptp.h
> @@ -4,6 +4,7 @@
> *
> * License: Dual MIT/GPL
> * Copyright (c) 2017 Microsemi Corporation
> + * Copyright 2020 NXP
> */
>
> #ifndef _MSCC_OCELOT_PTP_H_
> diff --git a/include/soc/mscc/ptp_ocelot.h b/include/soc/mscc/ptp_ocelot.h
> new file mode 100644
> index 0000000..b8d9c5b
> --- /dev/null
> +++ b/include/soc/mscc/ptp_ocelot.h
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
> +/*
> + * Microsemi Ocelot PTP clock driver
> + *
> + * License: Dual MIT/GPL
> + * Copyright 2020 NXP
> + */
> +
> +#ifndef _PTP_OCELOT_H_
> +#define _PTP_OCELOT_H_
> +
> +#include <soc/mscc/ocelot.h>
> +#include <linux/ptp_clock_kernel.h>
> +
> +#ifdef CONFIG_PTP_1588_CLOCK_OCELOT
And if you decide to allow building it as a module, you should change
this to "#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK_OCELOT)" to cover that
case too.
> +int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts);
> +int ocelot_init_timestamp(struct ocelot *ocelot);
> +int ocelot_deinit_timestamp(struct ocelot *ocelot);
> +#else
> +static inline int ocelot_ptp_gettime64(struct ptp_clock_info *ptp,
> + struct timespec64 *ts)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int ocelot_init_timestamp(struct ocelot *ocelot)
> +{
> + return -EOPNOTSUPP;
> +}
> +static inline int ocelot_deinit_timestamp(struct ocelot *ocelot)
> +{
> + return -EOPNOTSUPP;
> +}
> +#endif
> +#endif
> --
> 2.7.4
>
Thanks,
-Vladimir
Powered by blists - more mailing lists