Skip to content

Commit

Permalink
net: aquantia: add basic ptp_clock callbacks
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Egor Pomozov authored and David S. Miller committed Oct 24, 2019
1 parent 593f7b4 commit 910479a
Show file tree
Hide file tree
Showing 10 changed files with 403 additions and 30 deletions.
21 changes: 20 additions & 1 deletion drivers/net/ethernet/aquantia/atlantic/aq_hw.h
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
};

Expand Down Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_nic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
125 changes: 125 additions & 0 deletions drivers/net/ethernet/aquantia/atlantic/aq_ptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,108 @@

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,
Expand All @@ -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))) {
Expand All @@ -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:
Expand All @@ -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;
}
110 changes: 108 additions & 2 deletions drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
Original file line number Diff line number Diff line change
@@ -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. */
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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,
};
Loading

0 comments on commit 910479a

Please sign in to comment.