You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
664 lines
20 KiB
664 lines
20 KiB
From 83e7602f8870827a811276a007f5ed2a608284c9 Mon Sep 17 00:00:00 2001
|
|
From: Igor Russkikh <irusskik@redhat.com>
|
|
Date: Fri, 6 Nov 2020 18:36:46 -0500
|
|
Subject: [PATCH 029/139] [netdrv] net: aquantia: add support for PIN funcs
|
|
|
|
Message-id: <1604687916-15087-30-git-send-email-irusskik@redhat.com>
|
|
Patchwork-id: 338451
|
|
Patchwork-instance: patchwork
|
|
O-Subject: [RHEL8.4 BZ 1857861 029/139] net: aquantia: add support for PIN funcs
|
|
Bugzilla: 1857861
|
|
RH-Acked-by: David Arcari <darcari@redhat.com>
|
|
RH-Acked-by: John Linville <linville@redhat.com>
|
|
RH-Acked-by: Tony Camuso <tcamuso@redhat.com>
|
|
|
|
Bugzilla: http://bugzilla.redhat.com/1857861
|
|
|
|
commit 9c477032f7d0beafe592e65238d8fb79341e91dc
|
|
Author: Dmitry Bezrukov <dmitry.bezrukov@aquantia.com>
|
|
Date: Tue Oct 22 09:53:47 2019 +0000
|
|
|
|
net: aquantia: add support for PIN funcs
|
|
|
|
Depending on FW configuration we can manage from 0 to 3 PINs for periodic output
|
|
and from 0 to 1 ext ts PIN for getting TS for external event.
|
|
|
|
Ext TS PIN functionality is implemented via periodic timestamps polling
|
|
directly from PHY, because right now there is now way to receive the
|
|
PIN trigger interrupt from phy.
|
|
|
|
The polling interval is 15 milliseconds.
|
|
|
|
Co-developed-by: Egor Pomozov <epomozov@marvell.com>
|
|
Signed-off-by: Egor Pomozov <epomozov@marvell.com>
|
|
Co-developed-by: Pavel Belous <pavel.belous@aquantia.com>
|
|
Signed-off-by: Pavel Belous <pavel.belous@aquantia.com>
|
|
Signed-off-by: Dmitry Bezrukov <dmitry.bezrukov@aquantia.com>
|
|
Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com>
|
|
Signed-off-by: David S. Miller <davem@davemloft.net>
|
|
|
|
Signed-off-by: Igor Russkikh <irusskik@redhat.com>
|
|
|
|
Cc: David Arcari <darcari@redhat.com>
|
|
Cc: Igor Russkikh <irusskik@redhat.com>
|
|
Signed-off-by: Jan Stancek <jstancek@redhat.com>
|
|
---
|
|
drivers/net/ethernet/aquantia/atlantic/aq_hw.h | 10 +
|
|
drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 1 +
|
|
drivers/net/ethernet/aquantia/atlantic/aq_ptp.c | 339 +++++++++++++++++++++
|
|
drivers/net/ethernet/aquantia/atlantic/aq_ptp.h | 2 +
|
|
.../ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c | 63 +++-
|
|
.../aquantia/atlantic/hw_atl/hw_atl_utils.h | 8 +
|
|
6 files changed, 422 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
|
|
index 596ede85f81e..a5cf6e02d4f6 100644
|
|
--- a/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
|
|
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw.h
|
|
@@ -259,6 +259,16 @@ struct aq_hw_ops {
|
|
|
|
int (*hw_set_sys_clock)(struct aq_hw_s *self, u64 time, u64 ts);
|
|
|
|
+ int (*hw_ts_to_sys_clock)(struct aq_hw_s *self, u64 ts, u64 *time);
|
|
+
|
|
+ int (*hw_gpio_pulse)(struct aq_hw_s *self, u32 index, u64 start,
|
|
+ u32 period);
|
|
+
|
|
+ int (*hw_extts_gpio_enable)(struct aq_hw_s *self, u32 index,
|
|
+ u32 enable);
|
|
+
|
|
+ int (*hw_get_sync_ts)(struct aq_hw_s *self, u64 *ts);
|
|
+
|
|
u16 (*rx_extract_ts)(struct aq_hw_s *self, u8 *p, unsigned int len,
|
|
u64 *timestamp);
|
|
|
|
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
|
|
index 1e12cedee11e..433adc099e44 100644
|
|
--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
|
|
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
|
|
@@ -152,6 +152,7 @@ static int aq_nic_update_link_status(struct aq_nic_s *self)
|
|
aq_ptp_clock_init(self);
|
|
aq_ptp_tm_offset_set(self,
|
|
self->aq_hw->aq_link_status.mbps);
|
|
+ aq_ptp_link_change(self);
|
|
}
|
|
|
|
/* Driver has to update flow control settings on RX block
|
|
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
|
|
index 56613792abc8..3ec08415e53e 100644
|
|
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
|
|
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
|
|
@@ -15,10 +15,13 @@
|
|
#include "aq_nic.h"
|
|
#include "aq_ptp.h"
|
|
#include "aq_ring.h"
|
|
+#include "aq_phy.h"
|
|
#include "aq_filters.h"
|
|
|
|
#define AQ_PTP_TX_TIMEOUT (HZ * 10)
|
|
|
|
+#define POLL_SYNC_TIMER_MS 15
|
|
+
|
|
enum ptp_speed_offsets {
|
|
ptp_offset_idx_10 = 0,
|
|
ptp_offset_idx_100,
|
|
@@ -68,6 +71,12 @@ struct aq_ptp_s {
|
|
|
|
struct aq_rx_filter_l3l4 udp_filter;
|
|
struct aq_rx_filter_l2 eth_type_filter;
|
|
+
|
|
+ struct delayed_work poll_sync;
|
|
+ u32 poll_timeout_ms;
|
|
+
|
|
+ bool extts_pin_enabled;
|
|
+ u64 last_sync1588_ts;
|
|
};
|
|
|
|
struct ptp_tm_offset {
|
|
@@ -348,6 +357,168 @@ static void aq_ptp_convert_to_hwtstamp(struct aq_ptp_s *aq_ptp,
|
|
hwtstamp->hwtstamp = ns_to_ktime(timestamp);
|
|
}
|
|
|
|
+static int aq_ptp_hw_pin_conf(struct aq_nic_s *aq_nic, u32 pin_index, u64 start,
|
|
+ u64 period)
|
|
+{
|
|
+ if (period)
|
|
+ netdev_dbg(aq_nic->ndev,
|
|
+ "Enable GPIO %d pulsing, start time %llu, period %u\n",
|
|
+ pin_index, start, (u32)period);
|
|
+ else
|
|
+ netdev_dbg(aq_nic->ndev,
|
|
+ "Disable GPIO %d pulsing, start time %llu, period %u\n",
|
|
+ pin_index, start, (u32)period);
|
|
+
|
|
+ /* Notify hardware of request to being sending pulses.
|
|
+ * If period is ZERO then pulsen is disabled.
|
|
+ */
|
|
+ mutex_lock(&aq_nic->fwreq_mutex);
|
|
+ aq_nic->aq_hw_ops->hw_gpio_pulse(aq_nic->aq_hw, pin_index,
|
|
+ start, (u32)period);
|
|
+ mutex_unlock(&aq_nic->fwreq_mutex);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int aq_ptp_perout_pin_configure(struct ptp_clock_info *ptp,
|
|
+ struct ptp_clock_request *rq, int on)
|
|
+{
|
|
+ struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info);
|
|
+ struct ptp_clock_time *t = &rq->perout.period;
|
|
+ struct ptp_clock_time *s = &rq->perout.start;
|
|
+ struct aq_nic_s *aq_nic = aq_ptp->aq_nic;
|
|
+ u64 start, period;
|
|
+ u32 pin_index = rq->perout.index;
|
|
+
|
|
+ /* verify the request channel is there */
|
|
+ if (pin_index >= ptp->n_per_out)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* we cannot support periods greater
|
|
+ * than 4 seconds due to reg limit
|
|
+ */
|
|
+ if (t->sec > 4 || t->sec < 0)
|
|
+ return -ERANGE;
|
|
+
|
|
+ /* convert to unsigned 64b ns,
|
|
+ * verify we can put it in a 32b register
|
|
+ */
|
|
+ period = on ? t->sec * NSEC_PER_SEC + t->nsec : 0;
|
|
+
|
|
+ /* verify the value is in range supported by hardware */
|
|
+ if (period > U32_MAX)
|
|
+ return -ERANGE;
|
|
+ /* convert to unsigned 64b ns */
|
|
+ /* TODO convert to AQ time */
|
|
+ start = on ? s->sec * NSEC_PER_SEC + s->nsec : 0;
|
|
+
|
|
+ aq_ptp_hw_pin_conf(aq_nic, pin_index, start, period);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int aq_ptp_pps_pin_configure(struct ptp_clock_info *ptp,
|
|
+ struct ptp_clock_request *rq, int on)
|
|
+{
|
|
+ struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info);
|
|
+ struct aq_nic_s *aq_nic = aq_ptp->aq_nic;
|
|
+ u64 start, period;
|
|
+ u32 pin_index = 0;
|
|
+ u32 rest = 0;
|
|
+
|
|
+ /* verify the request channel is there */
|
|
+ if (pin_index >= ptp->n_per_out)
|
|
+ return -EINVAL;
|
|
+
|
|
+ aq_nic->aq_hw_ops->hw_get_ptp_ts(aq_nic->aq_hw, &start);
|
|
+ div_u64_rem(start, NSEC_PER_SEC, &rest);
|
|
+ period = on ? NSEC_PER_SEC : 0; /* PPS - pulse per second */
|
|
+ start = on ? start - rest + NSEC_PER_SEC *
|
|
+ (rest > 990000000LL ? 2 : 1) : 0;
|
|
+
|
|
+ aq_ptp_hw_pin_conf(aq_nic, pin_index, start, period);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void aq_ptp_extts_pin_ctrl(struct aq_ptp_s *aq_ptp)
|
|
+{
|
|
+ struct aq_nic_s *aq_nic = aq_ptp->aq_nic;
|
|
+ u32 enable = aq_ptp->extts_pin_enabled;
|
|
+
|
|
+ if (aq_nic->aq_hw_ops->hw_extts_gpio_enable)
|
|
+ aq_nic->aq_hw_ops->hw_extts_gpio_enable(aq_nic->aq_hw, 0,
|
|
+ enable);
|
|
+}
|
|
+
|
|
+static int aq_ptp_extts_pin_configure(struct ptp_clock_info *ptp,
|
|
+ struct ptp_clock_request *rq, int on)
|
|
+{
|
|
+ struct aq_ptp_s *aq_ptp = container_of(ptp, struct aq_ptp_s, ptp_info);
|
|
+
|
|
+ u32 pin_index = rq->extts.index;
|
|
+
|
|
+ if (pin_index >= ptp->n_ext_ts)
|
|
+ return -EINVAL;
|
|
+
|
|
+ aq_ptp->extts_pin_enabled = !!on;
|
|
+ if (on) {
|
|
+ aq_ptp->poll_timeout_ms = POLL_SYNC_TIMER_MS;
|
|
+ cancel_delayed_work_sync(&aq_ptp->poll_sync);
|
|
+ schedule_delayed_work(&aq_ptp->poll_sync,
|
|
+ msecs_to_jiffies(aq_ptp->poll_timeout_ms));
|
|
+ }
|
|
+
|
|
+ aq_ptp_extts_pin_ctrl(aq_ptp);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* aq_ptp_gpio_feature_enable
|
|
+ * @ptp: the ptp clock structure
|
|
+ * @rq: the requested feature to change
|
|
+ * @on: whether to enable or disable the feature
|
|
+ */
|
|
+static int aq_ptp_gpio_feature_enable(struct ptp_clock_info *ptp,
|
|
+ struct ptp_clock_request *rq, int on)
|
|
+{
|
|
+ switch (rq->type) {
|
|
+ case PTP_CLK_REQ_EXTTS:
|
|
+ return aq_ptp_extts_pin_configure(ptp, rq, on);
|
|
+ case PTP_CLK_REQ_PEROUT:
|
|
+ return aq_ptp_perout_pin_configure(ptp, rq, on);
|
|
+ case PTP_CLK_REQ_PPS:
|
|
+ return aq_ptp_pps_pin_configure(ptp, rq, on);
|
|
+ default:
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* aq_ptp_verify
|
|
+ * @ptp: the ptp clock structure
|
|
+ * @pin: index of the pin in question
|
|
+ * @func: the desired function to use
|
|
+ * @chan: the function channel index to use
|
|
+ */
|
|
+static int aq_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
|
|
+ enum ptp_pin_function func, unsigned int chan)
|
|
+{
|
|
+ /* verify the requested pin is there */
|
|
+ if (!ptp->pin_config || pin >= ptp->n_pins)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* enforce locked channels, no changing them */
|
|
+ if (chan != ptp->pin_config[pin].chan)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* we want to keep the functions locked as well */
|
|
+ if (func != ptp->pin_config[pin].func)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/* aq_ptp_tx_hwtstamp - utility function which checks for TX time stamp
|
|
* @adapter: the private adapter struct
|
|
*
|
|
@@ -859,6 +1030,8 @@ void aq_ptp_ring_free(struct aq_nic_s *aq_nic)
|
|
aq_ptp_skb_ring_release(&aq_ptp->skb_ring);
|
|
}
|
|
|
|
+#define MAX_PTP_GPIO_COUNT 4
|
|
+
|
|
static struct ptp_clock_info aq_ptp_clock = {
|
|
.owner = THIS_MODULE,
|
|
.name = "atlantic ptp",
|
|
@@ -870,7 +1043,9 @@ static struct ptp_clock_info aq_ptp_clock = {
|
|
.gettime64 = aq_ptp_gettime,
|
|
.settime64 = aq_ptp_settime,
|
|
.n_per_out = 0,
|
|
+ .enable = aq_ptp_gpio_feature_enable,
|
|
.n_pins = 0,
|
|
+ .verify = aq_ptp_verify,
|
|
.pin_config = NULL,
|
|
};
|
|
|
|
@@ -928,6 +1103,57 @@ static void aq_ptp_offset_init(const struct hw_aq_ptp_offset *offsets)
|
|
aq_ptp_offset_init_from_fw(offsets);
|
|
}
|
|
|
|
+static void aq_ptp_gpio_init(struct ptp_clock_info *info,
|
|
+ struct hw_aq_info *hw_info)
|
|
+{
|
|
+ struct ptp_pin_desc pin_desc[MAX_PTP_GPIO_COUNT];
|
|
+ u32 extts_pin_cnt = 0;
|
|
+ u32 out_pin_cnt = 0;
|
|
+ u32 i;
|
|
+
|
|
+ memset(pin_desc, 0, sizeof(pin_desc));
|
|
+
|
|
+ for (i = 0; i < MAX_PTP_GPIO_COUNT - 1; i++) {
|
|
+ if (hw_info->gpio_pin[i] ==
|
|
+ (GPIO_PIN_FUNCTION_PTP0 + out_pin_cnt)) {
|
|
+ snprintf(pin_desc[out_pin_cnt].name,
|
|
+ sizeof(pin_desc[out_pin_cnt].name),
|
|
+ "AQ_GPIO%d", i);
|
|
+ pin_desc[out_pin_cnt].index = out_pin_cnt;
|
|
+ pin_desc[out_pin_cnt].chan = out_pin_cnt;
|
|
+ pin_desc[out_pin_cnt++].func = PTP_PF_PEROUT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ info->n_per_out = out_pin_cnt;
|
|
+
|
|
+ if (hw_info->caps_ex & BIT(CAPS_EX_PHY_CTRL_TS_PIN)) {
|
|
+ extts_pin_cnt += 1;
|
|
+
|
|
+ snprintf(pin_desc[out_pin_cnt].name,
|
|
+ sizeof(pin_desc[out_pin_cnt].name),
|
|
+ "AQ_GPIO%d", out_pin_cnt);
|
|
+ pin_desc[out_pin_cnt].index = out_pin_cnt;
|
|
+ pin_desc[out_pin_cnt].chan = 0;
|
|
+ pin_desc[out_pin_cnt].func = PTP_PF_EXTTS;
|
|
+ }
|
|
+
|
|
+ info->n_pins = out_pin_cnt + extts_pin_cnt;
|
|
+ info->n_ext_ts = extts_pin_cnt;
|
|
+
|
|
+ if (!info->n_pins)
|
|
+ return;
|
|
+
|
|
+ info->pin_config = kcalloc(info->n_pins, sizeof(struct ptp_pin_desc),
|
|
+ GFP_KERNEL);
|
|
+
|
|
+ if (!info->pin_config)
|
|
+ return;
|
|
+
|
|
+ memcpy(info->pin_config, &pin_desc,
|
|
+ sizeof(struct ptp_pin_desc) * info->n_pins);
|
|
+}
|
|
+
|
|
void aq_ptp_clock_init(struct aq_nic_s *aq_nic)
|
|
{
|
|
struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp;
|
|
@@ -937,6 +1163,8 @@ void aq_ptp_clock_init(struct aq_nic_s *aq_nic)
|
|
aq_ptp_settime(&aq_ptp->ptp_info, &ts);
|
|
}
|
|
|
|
+static void aq_ptp_poll_sync_work_cb(struct work_struct *w);
|
|
+
|
|
int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec)
|
|
{
|
|
struct hw_atl_utils_mbox mbox;
|
|
@@ -975,6 +1203,7 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec)
|
|
spin_lock_init(&aq_ptp->ptp_ring_lock);
|
|
|
|
aq_ptp->ptp_info = aq_ptp_clock;
|
|
+ aq_ptp_gpio_init(&aq_ptp->ptp_info, &mbox.info);
|
|
clock = ptp_clock_register(&aq_ptp->ptp_info, &aq_nic->ndev->dev);
|
|
if (!clock || IS_ERR(clock)) {
|
|
netdev_err(aq_nic->ndev, "ptp_clock_register failed\n");
|
|
@@ -1001,6 +1230,7 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec)
|
|
aq_ptp_clock_init(aq_nic);
|
|
mutex_unlock(&aq_nic->fwreq_mutex);
|
|
|
|
+ INIT_DELAYED_WORK(&aq_ptp->poll_sync, &aq_ptp_poll_sync_work_cb);
|
|
aq_ptp->eth_type_filter.location =
|
|
aq_nic_reserve_filter(aq_nic, aq_rx_filter_ethertype);
|
|
aq_ptp->udp_filter.location =
|
|
@@ -1009,6 +1239,8 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec)
|
|
return 0;
|
|
|
|
err_exit:
|
|
+ if (aq_ptp)
|
|
+ kfree(aq_ptp->ptp_info.pin_config);
|
|
kfree(aq_ptp);
|
|
aq_nic->aq_ptp = NULL;
|
|
return err;
|
|
@@ -1035,11 +1267,14 @@ void aq_ptp_free(struct aq_nic_s *aq_nic)
|
|
aq_ptp->eth_type_filter.location);
|
|
aq_nic_release_filter(aq_nic, aq_rx_filter_l3l4,
|
|
aq_ptp->udp_filter.location);
|
|
+ cancel_delayed_work_sync(&aq_ptp->poll_sync);
|
|
/* disable ptp */
|
|
mutex_lock(&aq_nic->fwreq_mutex);
|
|
aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 0);
|
|
mutex_unlock(&aq_nic->fwreq_mutex);
|
|
|
|
+ kfree(aq_ptp->ptp_info.pin_config);
|
|
+
|
|
netif_napi_del(&aq_ptp->napi);
|
|
kfree(aq_ptp);
|
|
aq_nic->aq_ptp = NULL;
|
|
@@ -1049,3 +1284,107 @@ struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp)
|
|
{
|
|
return aq_ptp->ptp_clock;
|
|
}
|
|
+
|
|
+/* PTP external GPIO nanoseconds count */
|
|
+static uint64_t aq_ptp_get_sync1588_ts(struct aq_nic_s *aq_nic)
|
|
+{
|
|
+ u64 ts = 0;
|
|
+
|
|
+ if (aq_nic->aq_hw_ops->hw_get_sync_ts)
|
|
+ aq_nic->aq_hw_ops->hw_get_sync_ts(aq_nic->aq_hw, &ts);
|
|
+
|
|
+ return ts;
|
|
+}
|
|
+
|
|
+static void aq_ptp_start_work(struct aq_ptp_s *aq_ptp)
|
|
+{
|
|
+ if (aq_ptp->extts_pin_enabled) {
|
|
+ aq_ptp->poll_timeout_ms = POLL_SYNC_TIMER_MS;
|
|
+ aq_ptp->last_sync1588_ts =
|
|
+ aq_ptp_get_sync1588_ts(aq_ptp->aq_nic);
|
|
+ schedule_delayed_work(&aq_ptp->poll_sync,
|
|
+ msecs_to_jiffies(aq_ptp->poll_timeout_ms));
|
|
+ }
|
|
+}
|
|
+
|
|
+int aq_ptp_link_change(struct aq_nic_s *aq_nic)
|
|
+{
|
|
+ struct aq_ptp_s *aq_ptp = aq_nic->aq_ptp;
|
|
+
|
|
+ if (!aq_ptp)
|
|
+ return 0;
|
|
+
|
|
+ if (aq_nic->aq_hw->aq_link_status.mbps)
|
|
+ aq_ptp_start_work(aq_ptp);
|
|
+ else
|
|
+ cancel_delayed_work_sync(&aq_ptp->poll_sync);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static bool aq_ptp_sync_ts_updated(struct aq_ptp_s *aq_ptp, u64 *new_ts)
|
|
+{
|
|
+ struct aq_nic_s *aq_nic = aq_ptp->aq_nic;
|
|
+ u64 sync_ts2;
|
|
+ u64 sync_ts;
|
|
+
|
|
+ sync_ts = aq_ptp_get_sync1588_ts(aq_nic);
|
|
+
|
|
+ if (sync_ts != aq_ptp->last_sync1588_ts) {
|
|
+ sync_ts2 = aq_ptp_get_sync1588_ts(aq_nic);
|
|
+ if (sync_ts != sync_ts2) {
|
|
+ sync_ts = sync_ts2;
|
|
+ sync_ts2 = aq_ptp_get_sync1588_ts(aq_nic);
|
|
+ if (sync_ts != sync_ts2) {
|
|
+ netdev_err(aq_nic->ndev,
|
|
+ "%s: Unable to get correct GPIO TS",
|
|
+ __func__);
|
|
+ sync_ts = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *new_ts = sync_ts;
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static int aq_ptp_check_sync1588(struct aq_ptp_s *aq_ptp)
|
|
+{
|
|
+ struct aq_nic_s *aq_nic = aq_ptp->aq_nic;
|
|
+ u64 sync_ts;
|
|
+
|
|
+ /* Sync1588 pin was triggered */
|
|
+ if (aq_ptp_sync_ts_updated(aq_ptp, &sync_ts)) {
|
|
+ if (aq_ptp->extts_pin_enabled) {
|
|
+ struct ptp_clock_event ptp_event;
|
|
+ u64 time = 0;
|
|
+
|
|
+ aq_nic->aq_hw_ops->hw_ts_to_sys_clock(aq_nic->aq_hw,
|
|
+ sync_ts, &time);
|
|
+ ptp_event.index = aq_ptp->ptp_info.n_pins - 1;
|
|
+ ptp_event.timestamp = time;
|
|
+
|
|
+ ptp_event.type = PTP_CLOCK_EXTTS;
|
|
+ ptp_clock_event(aq_ptp->ptp_clock, &ptp_event);
|
|
+ }
|
|
+
|
|
+ aq_ptp->last_sync1588_ts = sync_ts;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void aq_ptp_poll_sync_work_cb(struct work_struct *w)
|
|
+{
|
|
+ struct delayed_work *dw = to_delayed_work(w);
|
|
+ struct aq_ptp_s *aq_ptp = container_of(dw, struct aq_ptp_s, poll_sync);
|
|
+
|
|
+ aq_ptp_check_sync1588(aq_ptp);
|
|
+
|
|
+ if (aq_ptp->extts_pin_enabled) {
|
|
+ unsigned long timeout = msecs_to_jiffies(aq_ptp->poll_timeout_ms);
|
|
+
|
|
+ schedule_delayed_work(&aq_ptp->poll_sync, timeout);
|
|
+ }
|
|
+}
|
|
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h
|
|
index 7a7f36f43ce0..3de4682f7c06 100644
|
|
--- a/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h
|
|
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_ptp.h
|
|
@@ -52,4 +52,6 @@ u16 aq_ptp_extract_ts(struct aq_nic_s *aq_nic, struct sk_buff *skb, u8 *p,
|
|
|
|
struct ptp_clock *aq_ptp_get_ptp_clock(struct aq_ptp_s *aq_ptp);
|
|
|
|
+int aq_ptp_link_change(struct aq_nic_s *aq_nic);
|
|
+
|
|
#endif /* AQ_PTP_H */
|
|
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
|
|
index 56cec2ea0af0..51ecf87e0198 100644
|
|
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
|
|
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
|
|
@@ -10,6 +10,7 @@
|
|
#include "../aq_hw_utils.h"
|
|
#include "../aq_ring.h"
|
|
#include "../aq_nic.h"
|
|
+#include "../aq_phy.h"
|
|
#include "hw_atl_b0.h"
|
|
#include "hw_atl_utils.h"
|
|
#include "hw_atl_llh.h"
|
|
@@ -1151,6 +1152,12 @@ static int hw_atl_b0_set_sys_clock(struct aq_hw_s *self, u64 time, u64 ts)
|
|
return hw_atl_b0_adj_sys_clock(self, delta);
|
|
}
|
|
|
|
+int hw_atl_b0_ts_to_sys_clock(struct aq_hw_s *self, u64 ts, u64 *time)
|
|
+{
|
|
+ *time = self->ptp_clk_offset + ts;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int hw_atl_b0_adj_clock_freq(struct aq_hw_s *self, s32 ppb)
|
|
{
|
|
struct hw_fw_request_iface fwreq;
|
|
@@ -1173,6 +1180,57 @@ static int hw_atl_b0_adj_clock_freq(struct aq_hw_s *self, s32 ppb)
|
|
return self->aq_fw_ops->send_fw_request(self, &fwreq, size);
|
|
}
|
|
|
|
+static int hw_atl_b0_gpio_pulse(struct aq_hw_s *self, u32 index,
|
|
+ u64 start, u32 period)
|
|
+{
|
|
+ struct hw_fw_request_iface fwreq;
|
|
+ size_t size;
|
|
+
|
|
+ memset(&fwreq, 0, sizeof(fwreq));
|
|
+
|
|
+ fwreq.msg_id = HW_AQ_FW_REQUEST_PTP_GPIO_CTRL;
|
|
+ fwreq.ptp_gpio_ctrl.index = index;
|
|
+ fwreq.ptp_gpio_ctrl.period = period;
|
|
+ /* Apply time offset */
|
|
+ fwreq.ptp_gpio_ctrl.start = start - self->ptp_clk_offset;
|
|
+
|
|
+ size = sizeof(fwreq.msg_id) + sizeof(fwreq.ptp_gpio_ctrl);
|
|
+ return self->aq_fw_ops->send_fw_request(self, &fwreq, size);
|
|
+}
|
|
+
|
|
+static int hw_atl_b0_extts_gpio_enable(struct aq_hw_s *self, u32 index,
|
|
+ u32 enable)
|
|
+{
|
|
+ /* Enable/disable Sync1588 GPIO Timestamping */
|
|
+ aq_phy_write_reg(self, MDIO_MMD_PCS, 0xc611, enable ? 0x71 : 0);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hw_atl_b0_get_sync_ts(struct aq_hw_s *self, u64 *ts)
|
|
+{
|
|
+ u64 sec_l;
|
|
+ u64 sec_h;
|
|
+ u64 nsec_l;
|
|
+ u64 nsec_h;
|
|
+
|
|
+ if (!ts)
|
|
+ return -1;
|
|
+
|
|
+ /* PTP external GPIO clock seconds count 15:0 */
|
|
+ sec_l = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc914);
|
|
+ /* PTP external GPIO clock seconds count 31:16 */
|
|
+ sec_h = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc915);
|
|
+ /* PTP external GPIO clock nanoseconds count 15:0 */
|
|
+ nsec_l = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc916);
|
|
+ /* PTP external GPIO clock nanoseconds count 31:16 */
|
|
+ nsec_h = aq_phy_read_reg(self, MDIO_MMD_PCS, 0xc917);
|
|
+
|
|
+ *ts = (nsec_h << 16) + nsec_l + ((sec_h << 16) + sec_l) * NSEC_PER_SEC;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static u16 hw_atl_b0_rx_extract_ts(struct aq_hw_s *self, u8 *p,
|
|
unsigned int len, u64 *timestamp)
|
|
{
|
|
@@ -1416,8 +1474,11 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
|
|
.hw_get_ptp_ts = hw_atl_b0_get_ptp_ts,
|
|
.hw_adj_sys_clock = hw_atl_b0_adj_sys_clock,
|
|
.hw_set_sys_clock = hw_atl_b0_set_sys_clock,
|
|
+ .hw_ts_to_sys_clock = hw_atl_b0_ts_to_sys_clock,
|
|
.hw_adj_clock_freq = hw_atl_b0_adj_clock_freq,
|
|
-
|
|
+ .hw_gpio_pulse = hw_atl_b0_gpio_pulse,
|
|
+ .hw_extts_gpio_enable = hw_atl_b0_extts_gpio_enable,
|
|
+ .hw_get_sync_ts = hw_atl_b0_get_sync_ts,
|
|
.rx_extract_ts = hw_atl_b0_rx_extract_ts,
|
|
.extract_hwts = hw_atl_b0_extract_hwts,
|
|
.hw_set_offload = hw_atl_b0_hw_offload_set,
|
|
diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
|
|
index 77132bda4696..37e6b696009d 100644
|
|
--- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
|
|
+++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
|
|
@@ -288,6 +288,12 @@ struct __packed offload_info {
|
|
};
|
|
|
|
/* Mailbox FW Request interface */
|
|
+struct __packed hw_fw_request_ptp_gpio_ctrl {
|
|
+ u32 index;
|
|
+ u32 period;
|
|
+ u64 start;
|
|
+};
|
|
+
|
|
struct __packed hw_fw_request_ptp_adj_freq {
|
|
u32 ns_mac;
|
|
u32 fns_mac;
|
|
@@ -303,6 +309,7 @@ struct __packed hw_fw_request_ptp_adj_clock {
|
|
int sign;
|
|
};
|
|
|
|
+#define HW_AQ_FW_REQUEST_PTP_GPIO_CTRL 0x11
|
|
#define HW_AQ_FW_REQUEST_PTP_ADJ_FREQ 0x12
|
|
#define HW_AQ_FW_REQUEST_PTP_ADJ_CLOCK 0x13
|
|
|
|
@@ -310,6 +317,7 @@ struct __packed hw_fw_request_iface {
|
|
u32 msg_id;
|
|
union {
|
|
/* PTP FW Request */
|
|
+ struct hw_fw_request_ptp_gpio_ctrl ptp_gpio_ctrl;
|
|
struct hw_fw_request_ptp_adj_freq ptp_adj_freq;
|
|
struct hw_fw_request_ptp_adj_clock ptp_adj_clock;
|
|
};
|
|
--
|
|
2.13.6
|
|
|