]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
net: aquantia: add basic ptp_clock callbacks
authorEgor Pomozov <epomozov@marvell.com>
Tue, 22 Oct 2019 09:53:27 +0000 (09:53 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 24 Oct 2019 16:51:22 +0000 (09:51 -0700)
Basic HW functions implemented for adjusting frequency,
adjusting time, getting and setting time.
With these callbacks we now do register ptp clock in the system.

Firmware interface parts are defined for PTP requests and interactions.
Enable/disable PTP counters in HW on clock register/unregister.

Signed-off-by: Egor Pomozov <epomozov@marvell.com>
Co-developed-by: Sergey Samoilenko <sergey.samoilenko@aquantia.com>
Signed-off-by: Sergey Samoilenko <sergey.samoilenko@aquantia.com>
Co-developed-by: Dmitry Bezrukov <dmitry.bezrukov@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>
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
drivers/net/ethernet/aquantia/atlantic/aq_nic.c
drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils_fw2x.c

index 53d7478689a08e8deb618bbb2fd94c76defa5f8c..121e633fc25b6413c3bc4fc6cda319885e291666 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * aQuantia Corporation Network Driver
- * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
  */
 
 /* File aq_hw.h: Declaration of abstract interface for NIC hardware specific
@@ -15,6 +15,9 @@
 #include "aq_rss.h"
 #include "hw_atl/hw_atl_utils.h"
 
+#define AQ_HW_MAC_COUNTER_HZ   312500000ll
+#define AQ_HW_PHY_COUNTER_HZ   160000000ll
+
 #define AQ_RX_FIRST_LOC_FVLANID     0U
 #define AQ_RX_LAST_LOC_FVLANID    15U
 #define AQ_RX_FIRST_LOC_FETHERT    16U
@@ -94,6 +97,7 @@ struct aq_stats_s {
 #define AQ_HW_FLAG_STOPPING    0x00000008U
 #define AQ_HW_FLAG_RESETTING   0x00000010U
 #define AQ_HW_FLAG_CLOSING     0x00000020U
+#define AQ_HW_PTP_AVAILABLE    0x01000000U
 #define AQ_HW_LINK_DOWN        0x04000000U
 #define AQ_HW_FLAG_ERR_UNPLUG  0x40000000U
 #define AQ_HW_FLAG_ERR_HW      0x80000000U
@@ -135,6 +139,7 @@ struct aq_hw_s {
        u32 rpc_addr;
        u32 rpc_tid;
        struct hw_atl_utils_fw_rpc rpc;
+       s64 ptp_clk_offset;
 };
 
 struct aq_ring_s;
@@ -235,6 +240,14 @@ struct aq_hw_ops {
        int (*hw_set_offload)(struct aq_hw_s *self,
                              struct aq_nic_cfg_s *aq_nic_cfg);
 
+       void (*hw_get_ptp_ts)(struct aq_hw_s *self, u64 *stamp);
+
+       int (*hw_adj_clock_freq)(struct aq_hw_s *self, s32 delta);
+
+       int (*hw_adj_sys_clock)(struct aq_hw_s *self, s64 delta);
+
+       int (*hw_set_sys_clock)(struct aq_hw_s *self, u64 time, u64 ts);
+
        int (*hw_set_fc)(struct aq_hw_s *self, u32 fc, u32 tc);
 };
 
@@ -267,6 +280,12 @@ struct aq_fw_ops {
        int (*set_power)(struct aq_hw_s *self, unsigned int power_state,
                         u8 *mac);
 
+       int (*send_fw_request)(struct aq_hw_s *self,
+                              const struct hw_fw_request_iface *fw_req,
+                              size_t size);
+
+       void (*enable_ptp)(struct aq_hw_s *self, int enable);
+
        int (*set_eee_rate)(struct aq_hw_s *self, u32 speed);
 
        int (*get_eee_rate)(struct aq_hw_s *self, u32 *rate,
index d283d0bc75a31a95add64f76254e65e670866439..dc9769fe762b0ade8b11f7ea2214ef75d04999c3 100644 (file)
@@ -146,6 +146,9 @@ static int aq_nic_update_link_status(struct aq_nic_s *self)
                        self->aq_hw->aq_link_status.mbps);
                aq_nic_update_interrupt_moderation_settings(self);
 
+               if (self->aq_ptp)
+                       aq_ptp_clock_init(self);
+
                /* Driver has to update flow control settings on RX block
                 * on any link event.
                 * We should query FW whether it negotiated FC.
index a320916cced3adb4fb855cea81e8832b820d017c..02c2a8cd121944823abfd1cbf2297cdd18918121 100644 (file)
 
 struct aq_ptp_s {
        struct aq_nic_s *aq_nic;
+       spinlock_t ptp_lock;
        struct ptp_clock *ptp_clock;
        struct ptp_clock_info ptp_info;
 };
 
+/* aq_ptp_adjfine
+ * @ptp: the ptp clock structure
+ * @ppb: parts per billion adjustment from base
+ *
+ * adjust the frequency of the ptp cycle counter by the
+ * indicated ppb from the base frequency.
+ */
+static int aq_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
+{
+       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;
+
+       mutex_lock(&aq_nic->fwreq_mutex);
+       aq_nic->aq_hw_ops->hw_adj_clock_freq(aq_nic->aq_hw,
+                                            scaled_ppm_to_ppb(scaled_ppm));
+       mutex_unlock(&aq_nic->fwreq_mutex);
+
+       return 0;
+}
+
+/* aq_ptp_adjtime
+ * @ptp: the ptp clock structure
+ * @delta: offset to adjust the cycle counter by
+ *
+ * adjust the timer by resetting the timecounter structure.
+ */
+static int aq_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+       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;
+       unsigned long flags;
+
+       spin_lock_irqsave(&aq_ptp->ptp_lock, flags);
+       aq_nic->aq_hw_ops->hw_adj_sys_clock(aq_nic->aq_hw, delta);
+       spin_unlock_irqrestore(&aq_ptp->ptp_lock, flags);
+
+       return 0;
+}
+
+/* aq_ptp_gettime
+ * @ptp: the ptp clock structure
+ * @ts: timespec structure to hold the current time value
+ *
+ * read the timecounter and return the correct value on ns,
+ * after converting it into a struct timespec.
+ */
+static int aq_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
+{
+       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;
+       unsigned long flags;
+       u64 ns;
+
+       spin_lock_irqsave(&aq_ptp->ptp_lock, flags);
+       aq_nic->aq_hw_ops->hw_get_ptp_ts(aq_nic->aq_hw, &ns);
+       spin_unlock_irqrestore(&aq_ptp->ptp_lock, flags);
+
+       *ts = ns_to_timespec64(ns);
+
+       return 0;
+}
+
+/* aq_ptp_settime
+ * @ptp: the ptp clock structure
+ * @ts: the timespec containing the new time for the cycle counter
+ *
+ * reset the timecounter to use a new base value instead of the kernel
+ * wall timer value.
+ */
+static int aq_ptp_settime(struct ptp_clock_info *ptp,
+                         const struct timespec64 *ts)
+{
+       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;
+       unsigned long flags;
+       u64 ns = timespec64_to_ns(ts);
+       u64 now;
+
+       spin_lock_irqsave(&aq_ptp->ptp_lock, flags);
+       aq_nic->aq_hw_ops->hw_get_ptp_ts(aq_nic->aq_hw, &now);
+       aq_nic->aq_hw_ops->hw_adj_sys_clock(aq_nic->aq_hw, (s64)ns - (s64)now);
+
+       spin_unlock_irqrestore(&aq_ptp->ptp_lock, flags);
+
+       return 0;
+}
+
 static struct ptp_clock_info aq_ptp_clock = {
        .owner          = THIS_MODULE,
        .name           = "atlantic ptp",
+       .max_adj        = 999999999,
        .n_ext_ts       = 0,
        .pps            = 0,
+       .adjfine        = aq_ptp_adjfine,
+       .adjtime        = aq_ptp_adjtime,
+       .gettime64      = aq_ptp_gettime,
+       .settime64      = aq_ptp_settime,
        .n_per_out      = 0,
        .n_pins         = 0,
        .pin_config     = NULL,
@@ -32,9 +125,20 @@ static struct ptp_clock_info aq_ptp_clock = {
 int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec)
 {
        struct hw_atl_utils_mbox mbox;
+       struct ptp_clock *clock;
        struct aq_ptp_s *aq_ptp;
        int err = 0;
 
+       if (!aq_nic->aq_hw_ops->hw_get_ptp_ts) {
+               aq_nic->aq_ptp = NULL;
+               return 0;
+       }
+
+       if (!aq_nic->aq_fw_ops->enable_ptp) {
+               aq_nic->aq_ptp = NULL;
+               return 0;
+       }
+
        hw_atl_utils_mpi_read_stats(aq_nic->aq_hw, &mbox);
 
        if (!(mbox.info.caps_ex & BIT(CAPS_EX_PHY_PTP_EN))) {
@@ -50,10 +154,26 @@ int aq_ptp_init(struct aq_nic_s *aq_nic, unsigned int idx_vec)
 
        aq_ptp->aq_nic = aq_nic;
 
+       spin_lock_init(&aq_ptp->ptp_lock);
+
        aq_ptp->ptp_info = aq_ptp_clock;
+       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");
+               err = PTR_ERR(clock);
+               goto err_exit;
+       }
+       aq_ptp->ptp_clock = clock;
 
        aq_nic->aq_ptp = aq_ptp;
 
+       /* enable ptp counter */
+       aq_utils_obj_set(&aq_nic->aq_hw->flags, AQ_HW_PTP_AVAILABLE);
+       mutex_lock(&aq_nic->fwreq_mutex);
+       aq_nic->aq_fw_ops->enable_ptp(aq_nic->aq_hw, 1);
+       aq_ptp_clock_init(aq_nic);
+       mutex_unlock(&aq_nic->fwreq_mutex);
+
        return 0;
 
 err_exit:
@@ -79,6 +199,11 @@ void aq_ptp_free(struct aq_nic_s *aq_nic)
        if (!aq_ptp)
                return;
 
+       /* 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);
        aq_nic->aq_ptp = NULL;
 }
index 2ad3fa6316ce3271ecb7be8081fdcf3e505e98d0..881caa8ee319d88ff9577358414658b7c85e8419 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * aQuantia Corporation Network Driver
- * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
  */
 
 /* File hw_atl_b0.c: Definition of Atlantic hardware specific functions. */
@@ -49,6 +49,8 @@
        .mac_regs_count = 88,             \
        .hw_alive_check_addr = 0x10U
 
+#define FRAC_PER_NS 0x100000000LL
+
 const struct aq_hw_caps_s hw_atl_b0_caps_aqc100 = {
        DEFAULT_B0_BOARD_BASIC_CAPABILITIES,
        .media_type = AQ_HW_MEDIA_TYPE_FIBRE,
@@ -1005,6 +1007,104 @@ static int hw_atl_b0_hw_ring_rx_stop(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
+#define get_ptp_ts_val_u64(self, indx) \
+       ((u64)(hw_atl_pcs_ptp_clock_get(self, indx) & 0xffff))
+
+static void hw_atl_b0_get_ptp_ts(struct aq_hw_s *self, u64 *stamp)
+{
+       u64 ns;
+
+       hw_atl_pcs_ptp_clock_read_enable(self, 1);
+       hw_atl_pcs_ptp_clock_read_enable(self, 0);
+       ns = (get_ptp_ts_val_u64(self, 0) +
+             (get_ptp_ts_val_u64(self, 1) << 16)) * NSEC_PER_SEC +
+            (get_ptp_ts_val_u64(self, 3) +
+             (get_ptp_ts_val_u64(self, 4) << 16));
+
+       *stamp = ns + self->ptp_clk_offset;
+}
+
+static void hw_atl_b0_adj_params_get(u64 freq, s64 adj, u32 *ns, u32 *fns)
+{
+       /* For accuracy, the digit is extended */
+       s64 base_ns = ((adj + NSEC_PER_SEC) * NSEC_PER_SEC);
+       u64 nsi_frac = 0;
+       u64 nsi;
+
+       base_ns = div64_s64(base_ns, freq);
+       nsi = div64_u64(base_ns, NSEC_PER_SEC);
+
+       if (base_ns != nsi * NSEC_PER_SEC) {
+               s64 divisor = div64_s64((s64)NSEC_PER_SEC * NSEC_PER_SEC,
+                                       base_ns - nsi * NSEC_PER_SEC);
+               nsi_frac = div64_s64(FRAC_PER_NS * NSEC_PER_SEC, divisor);
+       }
+
+       *ns = (u32)nsi;
+       *fns = (u32)nsi_frac;
+}
+
+static void
+hw_atl_b0_mac_adj_param_calc(struct hw_fw_request_ptp_adj_freq *ptp_adj_freq,
+                            u64 phyfreq, u64 macfreq)
+{
+       s64 adj_fns_val;
+       s64 fns_in_sec_phy = phyfreq * (ptp_adj_freq->fns_phy +
+                                       FRAC_PER_NS * ptp_adj_freq->ns_phy);
+       s64 fns_in_sec_mac = macfreq * (ptp_adj_freq->fns_mac +
+                                       FRAC_PER_NS * ptp_adj_freq->ns_mac);
+       s64 fault_in_sec_phy = FRAC_PER_NS * NSEC_PER_SEC - fns_in_sec_phy;
+       s64 fault_in_sec_mac = FRAC_PER_NS * NSEC_PER_SEC - fns_in_sec_mac;
+       /* MAC MCP counter freq is macfreq / 4 */
+       s64 diff_in_mcp_overflow = (fault_in_sec_mac - fault_in_sec_phy) *
+                                  4 * FRAC_PER_NS;
+
+       diff_in_mcp_overflow = div64_s64(diff_in_mcp_overflow,
+                                        AQ_HW_MAC_COUNTER_HZ);
+       adj_fns_val = (ptp_adj_freq->fns_mac + FRAC_PER_NS *
+                      ptp_adj_freq->ns_mac) + diff_in_mcp_overflow;
+
+       ptp_adj_freq->mac_ns_adj = div64_s64(adj_fns_val, FRAC_PER_NS);
+       ptp_adj_freq->mac_fns_adj = adj_fns_val - ptp_adj_freq->mac_ns_adj *
+                                   FRAC_PER_NS;
+}
+
+static int hw_atl_b0_adj_sys_clock(struct aq_hw_s *self, s64 delta)
+{
+       self->ptp_clk_offset += delta;
+
+       return 0;
+}
+
+static int hw_atl_b0_set_sys_clock(struct aq_hw_s *self, u64 time, u64 ts)
+{
+       s64 delta = time - (self->ptp_clk_offset + ts);
+
+       return hw_atl_b0_adj_sys_clock(self, delta);
+}
+
+static int hw_atl_b0_adj_clock_freq(struct aq_hw_s *self, s32 ppb)
+{
+       struct hw_fw_request_iface fwreq;
+       size_t size;
+
+       memset(&fwreq, 0, sizeof(fwreq));
+
+       fwreq.msg_id = HW_AQ_FW_REQUEST_PTP_ADJ_FREQ;
+       hw_atl_b0_adj_params_get(AQ_HW_MAC_COUNTER_HZ, ppb,
+                                &fwreq.ptp_adj_freq.ns_mac,
+                                &fwreq.ptp_adj_freq.fns_mac);
+       hw_atl_b0_adj_params_get(AQ_HW_PHY_COUNTER_HZ, ppb,
+                                &fwreq.ptp_adj_freq.ns_phy,
+                                &fwreq.ptp_adj_freq.fns_phy);
+       hw_atl_b0_mac_adj_param_calc(&fwreq.ptp_adj_freq,
+                                    AQ_HW_PHY_COUNTER_HZ,
+                                    AQ_HW_MAC_COUNTER_HZ);
+
+       size = sizeof(fwreq.msg_id) + sizeof(fwreq.ptp_adj_freq);
+       return self->aq_fw_ops->send_fw_request(self, &fwreq, size);
+}
+
 static int hw_atl_b0_hw_fl3l4_clear(struct aq_hw_s *self,
                                    struct aq_rx_filter_l3l4 *data)
 {
@@ -1177,6 +1277,12 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
        .hw_get_regs                 = hw_atl_utils_hw_get_regs,
        .hw_get_hw_stats             = hw_atl_utils_get_hw_stats,
        .hw_get_fw_version           = hw_atl_utils_get_fw_version,
-       .hw_set_offload              = hw_atl_b0_hw_offload_set,
+
+       .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_adj_clock_freq       = hw_atl_b0_adj_clock_freq,
+
+       .hw_set_offload          = hw_atl_b0_hw_offload_set,
        .hw_set_fc                   = hw_atl_b0_set_fc,
 };
index 6f340695e6bd2383b316a440e3e9f1e8e20ca1a2..eb982288fc52f6dd937d84ceae5d1e7f69a400da 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * aQuantia Corporation Network Driver
- * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
  */
 
 /* File hw_atl_llh.c: Definitions of bitfield and register access functions for
@@ -1526,6 +1526,20 @@ void hw_atl_reg_glb_cpu_scratch_scp_set(struct aq_hw_s *aq_hw,
                        glb_cpu_scratch_scp);
 }
 
+void hw_atl_pcs_ptp_clock_read_enable(struct aq_hw_s *aq_hw,
+                                     u32 ptp_clock_read_enable)
+{
+       aq_hw_write_reg_bit(aq_hw, HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_ADR,
+                           HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_MSK,
+                           HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_SHIFT,
+                           ptp_clock_read_enable);
+}
+
+u32 hw_atl_pcs_ptp_clock_get(struct aq_hw_s *aq_hw, u32 index)
+{
+       return aq_hw_read_reg(aq_hw, HW_ATL_PCS_PTP_TS_VAL_ADDR(index));
+}
+
 void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr)
 {
        aq_hw_write_reg_bit(aq_hw, HW_ATL_MCP_UP_FORCE_INTERRUPT_ADR,
index c3ee278c3747c3635ab4035affe96b0addb6dc3b..7753ab860c954a6c6a9a65d3c96cb27b23108e1f 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * aQuantia Corporation Network Driver
- * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
  */
 
 /* File hw_atl_llh.h: Declarations of bitfield and register access functions for
@@ -715,6 +715,12 @@ void hw_atl_msm_reg_wr_strobe_set(struct aq_hw_s *aq_hw, u32 reg_wr_strobe);
 /* set pci register reset disable */
 void hw_atl_pci_pci_reg_res_dis_set(struct aq_hw_s *aq_hw, u32 pci_reg_res_dis);
 
+/* pcs */
+void hw_atl_pcs_ptp_clock_read_enable(struct aq_hw_s *aq_hw,
+                                     u32 ptp_clock_read_enable);
+
+u32 hw_atl_pcs_ptp_clock_get(struct aq_hw_s *aq_hw, u32 index);
+
 /* set uP Force Interrupt */
 void hw_atl_mcp_up_force_intr_set(struct aq_hw_s *aq_hw, u32 up_force_intr);
 
index 35887ad89025aa992ec16dc149e5ff0c12abd888..65fb74a4d5ea94824061872fe454ccfbd0b3d7f6 100644 (file)
@@ -1,7 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * aQuantia Corporation Network Driver
- * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
  */
 
 /* File hw_atl_llh_internal.h: Preprocessor definitions
 /* default value of bitfield register write strobe */
 #define HW_ATL_MSM_REG_WR_STROBE_DEFAULT 0x0
 
+/* register address for bitfield PTP Digital Clock Read Enable */
+#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_ADR 0x00004628
+/* bitmask for bitfield PTP Digital Clock Read Enable */
+#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_MSK 0x00000010
+/* inverted bitmask for bitfield PTP Digital Clock Read Enable */
+#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_MSKN 0xFFFFFFEF
+/* lower bit position of bitfield PTP Digital Clock Read Enable */
+#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_SHIFT 4
+/* width of bitfield PTP Digital Clock Read Enable */
+#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_WIDTH 1
+/* default value of bitfield PTP Digital Clock Read Enable */
+#define HW_ATL_PCS_PTP_CLOCK_READ_ENABLE_DEFAULT 0x0
+
+/* register address for ptp counter reading */
+#define HW_ATL_PCS_PTP_TS_VAL_ADDR(index) (0x00004900 + (index) * 0x4)
+
 /* mif soft reset bitfield definitions
  * preprocessor definitions for the bitfield "soft reset".
  * port="pif_glb_res_i"
index 32512539ae86c7acb4a26f8b1fb5b3d80519c470..6fc5640065bd81a3b8ac69b06effc5d9b0138bc2 100644 (file)
@@ -327,8 +327,7 @@ err_exit:
        return err;
 }
 
-static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p,
-                                        u32 cnt)
+int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p, u32 cnt)
 {
        u32 val;
        int err = 0;
@@ -964,4 +963,6 @@ const struct aq_fw_ops aq_fw_1x_ops = {
        .set_eee_rate = NULL,
        .get_eee_rate = NULL,
        .set_flow_control = NULL,
+       .send_fw_request = NULL,
+       .enable_ptp = NULL,
 };
index 766e02c7fd4e7d4f4de43e1f5170d1c844d3084b..f2eb94f298e2e271a89fcc10eafdbaaca179b243 100644 (file)
@@ -279,6 +279,34 @@ struct __packed offload_info {
        u8 buf[0];
 };
 
+/* Mailbox FW Request interface */
+struct __packed hw_fw_request_ptp_adj_freq {
+       u32 ns_mac;
+       u32 fns_mac;
+       u32 ns_phy;
+       u32 fns_phy;
+       u32 mac_ns_adj;
+       u32 mac_fns_adj;
+};
+
+struct __packed hw_fw_request_ptp_adj_clock {
+       u32 ns;
+       u32 sec;
+       int sign;
+};
+
+#define HW_AQ_FW_REQUEST_PTP_ADJ_FREQ           0x12
+#define HW_AQ_FW_REQUEST_PTP_ADJ_CLOCK          0x13
+
+struct __packed hw_fw_request_iface {
+       u32 msg_id;
+       union {
+               /* PTP FW Request */
+               struct hw_fw_request_ptp_adj_freq ptp_adj_freq;
+               struct hw_fw_request_ptp_adj_clock ptp_adj_clock;
+       };
+};
+
 enum hw_atl_rx_action_with_traffic {
        HW_ATL_RX_DISCARD,
        HW_ATL_RX_HOST,
@@ -561,6 +589,8 @@ struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self);
 int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
                                  u32 *p, u32 cnt);
 
+int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p, u32 cnt);
+
 int hw_atl_utils_fw_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac);
 
 int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size);
index 7bc51f8d6f2fcd0c0016e1cc6c3f8c04247455b7..f649ac949d06b4e8e77d746788869b71e13ac259 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
  * aQuantia Corporation Network Driver
- * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
+ * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
  */
 
 /* File hw_atl_utils_fw2x.c: Definition of firmware 2.x functions for
 #include "hw_atl_utils.h"
 #include "hw_atl_llh.h"
 
-#define HW_ATL_FW2X_MPI_RPC_ADDR        0x334
+#define HW_ATL_FW2X_MPI_RPC_ADDR         0x334
 
-#define HW_ATL_FW2X_MPI_MBOX_ADDR       0x360
-#define HW_ATL_FW2X_MPI_EFUSE_ADDR     0x364
-#define HW_ATL_FW2X_MPI_CONTROL_ADDR   0x368
-#define HW_ATL_FW2X_MPI_CONTROL2_ADDR  0x36C
-#define HW_ATL_FW2X_MPI_STATE_ADDR     0x370
-#define HW_ATL_FW2X_MPI_STATE2_ADDR     0x374
+#define HW_ATL_FW2X_MPI_MBOX_ADDR        0x360
+#define HW_ATL_FW2X_MPI_EFUSE_ADDR       0x364
+#define HW_ATL_FW2X_MPI_CONTROL_ADDR     0x368
+#define HW_ATL_FW2X_MPI_CONTROL2_ADDR    0x36C
+#define HW_ATL_FW2X_MPI_STATE_ADDR       0x370
+#define HW_ATL_FW2X_MPI_STATE2_ADDR      0x374
+
+#define HW_ATL_FW3X_EXT_CONTROL_ADDR     0x378
+#define HW_ATL_FW3X_EXT_STATE_ADDR       0x37c
 
 #define HW_ATL_FW2X_CAP_PAUSE            BIT(CAPS_HI_PAUSE)
 #define HW_ATL_FW2X_CAP_ASYM_PAUSE       BIT(CAPS_HI_ASYMMETRIC_PAUSE)
@@ -444,6 +447,54 @@ err_exit:
        return err;
 }
 
+static int aq_fw2x_send_fw_request(struct aq_hw_s *self,
+                                  const struct hw_fw_request_iface *fw_req,
+                                  size_t size)
+{
+       u32 ctrl2, orig_ctrl2;
+       u32 dword_cnt;
+       int err = 0;
+       u32 val;
+
+       /* Write data to drvIface Mailbox */
+       dword_cnt = size / sizeof(u32);
+       if (size % sizeof(u32))
+               dword_cnt++;
+       err = hw_atl_utils_fw_upload_dwords(self, aq_fw2x_rpc_get(self),
+                                           (void *)fw_req, dword_cnt);
+       if (err < 0)
+               goto err_exit;
+
+       /* Toggle statistics bit for FW to update */
+       ctrl2 = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
+       orig_ctrl2 = ctrl2 & BIT(CAPS_HI_FW_REQUEST);
+       ctrl2 = ctrl2 ^ BIT(CAPS_HI_FW_REQUEST);
+       aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, ctrl2);
+
+       /* Wait FW to report back */
+       err = readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val,
+                                       orig_ctrl2 != (val &
+                                                      BIT(CAPS_HI_FW_REQUEST)),
+                                       1U, 10000U);
+
+err_exit:
+       return err;
+}
+
+static void aq_fw3x_enable_ptp(struct aq_hw_s *self, int enable)
+{
+       u32 ptp_opts = aq_hw_read_reg(self, HW_ATL_FW3X_EXT_STATE_ADDR);
+       u32 all_ptp_features = BIT(CAPS_EX_PHY_PTP_EN) |
+                                                  BIT(CAPS_EX_PTP_GPIO_EN);
+
+       if (enable)
+               ptp_opts |= all_ptp_features;
+       else
+               ptp_opts &= ~all_ptp_features;
+
+       aq_hw_write_reg(self, HW_ATL_FW3X_EXT_CONTROL_ADDR, ptp_opts);
+}
+
 static int aq_fw2x_set_eee_rate(struct aq_hw_s *self, u32 speed)
 {
        u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
@@ -534,19 +585,21 @@ static u32 aq_fw2x_state2_get(struct aq_hw_s *self)
 }
 
 const struct aq_fw_ops aq_fw_2x_ops = {
-       .init = aq_fw2x_init,
-       .deinit = aq_fw2x_deinit,
-       .reset = NULL,
-       .renegotiate = aq_fw2x_renegotiate,
-       .get_mac_permanent = aq_fw2x_get_mac_permanent,
-       .set_link_speed = aq_fw2x_set_link_speed,
-       .set_state = aq_fw2x_set_state,
+       .init               = aq_fw2x_init,
+       .deinit             = aq_fw2x_deinit,
+       .reset              = NULL,
+       .renegotiate        = aq_fw2x_renegotiate,
+       .get_mac_permanent  = aq_fw2x_get_mac_permanent,
+       .set_link_speed     = aq_fw2x_set_link_speed,
+       .set_state          = aq_fw2x_set_state,
        .update_link_status = aq_fw2x_update_link_status,
-       .update_stats = aq_fw2x_update_stats,
-       .get_phy_temp = aq_fw2x_get_phy_temp,
-       .set_power = aq_fw2x_set_power,
-       .set_eee_rate = aq_fw2x_set_eee_rate,
-       .get_eee_rate = aq_fw2x_get_eee_rate,
-       .set_flow_control = aq_fw2x_set_flow_control,
-       .get_flow_control = aq_fw2x_get_flow_control
+       .update_stats       = aq_fw2x_update_stats,
+       .get_phy_temp       = aq_fw2x_get_phy_temp,
+       .set_power          = aq_fw2x_set_power,
+       .set_eee_rate       = aq_fw2x_set_eee_rate,
+       .get_eee_rate       = aq_fw2x_get_eee_rate,
+       .set_flow_control   = aq_fw2x_set_flow_control,
+       .get_flow_control   = aq_fw2x_get_flow_control,
+       .send_fw_request    = aq_fw2x_send_fw_request,
+       .enable_ptp         = aq_fw3x_enable_ptp,
 };