Skip to content

Commit

Permalink
Merge branch 'Add-PTP-support-for-Octeontx2'
Browse files Browse the repository at this point in the history
Subbaraya Sundeep says:

====================
Add PTP support for Octeontx2

This patchset adds PTP support for Octeontx2 platform.
PTP is an independent coprocessor block from which
CGX block fetches timestamp and prepends it to the
packet before sending to NIX block. Patches are as
follows:

Patch 1: Patch to enable/disable packet timstamping
         in CGX upon mailbox request. It also adjusts
         packet parser (NPC) for the 8 bytes timestamp
         appearing before the packet.

Patch 2: Patch adding PTP pci driver which configures
         the PTP block and hooks up to RVU AF driver.
         It also exposes a mailbox call to adjust PTP
         hardware clock.

Patch 3: Patch adding PTP clock driver for PF netdev.

v8:
 Added missing header file reported by kernel test robot
 in patch 2
v7:
 As per Jesse Brandeburg comments:
 Simplified functions in patch 1
 Replaced magic numbers with macros
 Added Copyrights
 Added code comments wherever required
 Modified commit description of patch 2
v6:
 Resent after net-next is open
v5:
 As suggested by David separated the fix (adding rtnl lock/unlock)
 and submitted to net.
 https://www.spinics.net/lists/netdev/msg669617.html
v4:
 Added rtnl_lock/unlock in otx2_reset to protect against
 network stack ndo_open and close calls
 Added NULL check after ptp_clock_register in otx2_ptp.c
v3:
 Fixed sparse error in otx2_txrx.c
 Removed static inlines in otx2_txrx.c
v2:
 Fixed kernel build robot reported error by
 adding timecounter.h to otx2_common.h
====================

Acked-by: Richard Cochran <richardcochran@gmail.com>
Acked-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 25, 2020
2 parents 373c15c + c9c12d3 commit 64d123f
Show file tree
Hide file tree
Showing 20 changed files with 1,031 additions and 10 deletions.
2 changes: 1 addition & 1 deletion drivers/net/ethernet/marvell/octeontx2/af/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ obj-$(CONFIG_OCTEONTX2_AF) += octeontx2_af.o

octeontx2_mbox-y := mbox.o
octeontx2_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \
rvu_reg.o rvu_npc.o rvu_debugfs.o
rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o
29 changes: 29 additions & 0 deletions drivers/net/ethernet/marvell/octeontx2/af/cgx.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,35 @@ static void cgx_lmac_pause_frm_config(struct cgx *cgx, int lmac_id, bool enable)
}
}

void cgx_lmac_ptp_config(void *cgxd, int lmac_id, bool enable)
{
struct cgx *cgx = cgxd;
u64 cfg;

if (!cgx)
return;

if (enable) {
/* Enable inbound PTP timestamping */
cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
cfg |= CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE;
cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);

cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
cfg |= CGX_SMUX_RX_FRM_CTL_PTP_MODE;
cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg);
} else {
/* Disable inbound PTP stamping */
cfg = cgx_read(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL);
cfg &= ~CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE;
cgx_write(cgx, lmac_id, CGXX_GMP_GMI_RXX_FRM_CTL, cfg);

cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL);
cfg &= ~CGX_SMUX_RX_FRM_CTL_PTP_MODE;
cgx_write(cgx, lmac_id, CGXX_SMUX_RX_FRM_CTL, cfg);
}
}

/* CGX Firmware interface low level support */
static int cgx_fwi_cmd_send(u64 req, u64 *resp, struct lmac *lmac)
{
Expand Down
4 changes: 4 additions & 0 deletions drivers/net/ethernet/marvell/octeontx2/af/cgx.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,10 @@

#define CGXX_SMUX_RX_FRM_CTL 0x20020
#define CGX_SMUX_RX_FRM_CTL_CTL_BCK BIT_ULL(3)
#define CGX_SMUX_RX_FRM_CTL_PTP_MODE BIT_ULL(12)
#define CGXX_GMP_GMI_RXX_FRM_CTL 0x38028
#define CGX_GMP_GMI_RXX_FRM_CTL_CTL_BCK BIT_ULL(3)
#define CGX_GMP_GMI_RXX_FRM_CTL_PTP_MODE BIT_ULL(12)
#define CGXX_SMUX_TX_CTL 0x20178
#define CGXX_SMUX_TX_PAUSE_PKT_TIME 0x20110
#define CGXX_SMUX_TX_PAUSE_PKT_INTERVAL 0x20120
Expand Down Expand Up @@ -139,4 +141,6 @@ int cgx_lmac_get_pause_frm(void *cgxd, int lmac_id,
u8 *tx_pause, u8 *rx_pause);
int cgx_lmac_set_pause_frm(void *cgxd, int lmac_id,
u8 tx_pause, u8 rx_pause);
void cgx_lmac_ptp_config(void *cgxd, int lmac_id, bool enable);

#endif /* CGX_H */
21 changes: 21 additions & 0 deletions drivers/net/ethernet/marvell/octeontx2/af/mbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ M(ATTACH_RESOURCES, 0x002, attach_resources, rsrc_attach, msg_rsp) \
M(DETACH_RESOURCES, 0x003, detach_resources, rsrc_detach, msg_rsp) \
M(MSIX_OFFSET, 0x005, msix_offset, msg_req, msix_offset_rsp) \
M(VF_FLR, 0x006, vf_flr, msg_req, msg_rsp) \
M(PTP_OP, 0x007, ptp_op, ptp_req, ptp_rsp) \
M(GET_HW_CAP, 0x008, get_hw_cap, msg_req, get_hw_cap_rsp) \
/* CGX mbox IDs (range 0x200 - 0x3FF) */ \
M(CGX_START_RXTX, 0x200, cgx_start_rxtx, msg_req, msg_rsp) \
Expand All @@ -143,6 +144,8 @@ M(CGX_STOP_LINKEVENTS, 0x208, cgx_stop_linkevents, msg_req, msg_rsp) \
M(CGX_GET_LINKINFO, 0x209, cgx_get_linkinfo, msg_req, cgx_link_info_msg) \
M(CGX_INTLBK_ENABLE, 0x20A, cgx_intlbk_enable, msg_req, msg_rsp) \
M(CGX_INTLBK_DISABLE, 0x20B, cgx_intlbk_disable, msg_req, msg_rsp) \
M(CGX_PTP_RX_ENABLE, 0x20C, cgx_ptp_rx_enable, msg_req, msg_rsp) \
M(CGX_PTP_RX_DISABLE, 0x20D, cgx_ptp_rx_disable, msg_req, msg_rsp) \
M(CGX_CFG_PAUSE_FRM, 0x20E, cgx_cfg_pause_frm, cgx_pause_frm_cfg, \
cgx_pause_frm_cfg) \
/* NPA mbox IDs (range 0x400 - 0x5FF) */ \
Expand Down Expand Up @@ -213,6 +216,8 @@ M(NIX_LSO_FORMAT_CFG, 0x8011, nix_lso_format_cfg, \
nix_lso_format_cfg, \
nix_lso_format_cfg_rsp) \
M(NIX_RXVLAN_ALLOC, 0x8012, nix_rxvlan_alloc, msg_req, msg_rsp) \
M(NIX_LF_PTP_TX_ENABLE, 0x8013, nix_lf_ptp_tx_enable, msg_req, msg_rsp) \
M(NIX_LF_PTP_TX_DISABLE, 0x8014, nix_lf_ptp_tx_disable, msg_req, msg_rsp) \
M(NIX_BP_ENABLE, 0x8016, nix_bp_enable, nix_bp_cfg_req, \
nix_bp_cfg_rsp) \
M(NIX_BP_DISABLE, 0x8017, nix_bp_disable, nix_bp_cfg_req, msg_rsp) \
Expand Down Expand Up @@ -858,4 +863,20 @@ struct npc_get_kex_cfg_rsp {
u8 mkex_pfl_name[MKEX_NAME_LEN];
};

enum ptp_op {
PTP_OP_ADJFINE = 0,
PTP_OP_GET_CLOCK = 1,
};

struct ptp_req {
struct mbox_msghdr hdr;
u8 op;
s64 scaled_ppm;
};

struct ptp_rsp {
struct mbox_msghdr hdr;
u64 clk;
};

#endif /* MBOX_H */
275 changes: 275 additions & 0 deletions drivers/net/ethernet/marvell/octeontx2/af/ptp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
// SPDX-License-Identifier: GPL-2.0
/* Marvell PTP driver
*
* Copyright (C) 2020 Marvell International Ltd.
*/

#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pci.h>

#include "ptp.h"
#include "mbox.h"
#include "rvu.h"

#define DRV_NAME "Marvell PTP Driver"

#define PCI_DEVID_OCTEONTX2_PTP 0xA00C
#define PCI_SUBSYS_DEVID_OCTX2_98xx_PTP 0xB100
#define PCI_SUBSYS_DEVID_OCTX2_96XX_PTP 0xB200
#define PCI_SUBSYS_DEVID_OCTX2_95XX_PTP 0xB300
#define PCI_SUBSYS_DEVID_OCTX2_LOKI_PTP 0xB400
#define PCI_SUBSYS_DEVID_OCTX2_95MM_PTP 0xB500
#define PCI_DEVID_OCTEONTX2_RST 0xA085

#define PCI_PTP_BAR_NO 0
#define PCI_RST_BAR_NO 0

#define PTP_CLOCK_CFG 0xF00ULL
#define PTP_CLOCK_CFG_PTP_EN BIT_ULL(0)
#define PTP_CLOCK_LO 0xF08ULL
#define PTP_CLOCK_HI 0xF10ULL
#define PTP_CLOCK_COMP 0xF18ULL

#define RST_BOOT 0x1600ULL
#define RST_MUL_BITS GENMASK_ULL(38, 33)
#define CLOCK_BASE_RATE 50000000ULL

static u64 get_clock_rate(void)
{
u64 cfg, ret = CLOCK_BASE_RATE * 16;
struct pci_dev *pdev;
void __iomem *base;

/* To get the input clock frequency with which PTP co-processor
* block is running the base frequency(50 MHz) needs to be multiplied
* with multiplier bits present in RST_BOOT register of RESET block.
* Hence below code gets the multiplier bits from the RESET PCI
* device present in the system.
*/
pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
PCI_DEVID_OCTEONTX2_RST, NULL);
if (!pdev)
goto error;

base = pci_ioremap_bar(pdev, PCI_RST_BAR_NO);
if (!base)
goto error_put_pdev;

cfg = readq(base + RST_BOOT);
ret = CLOCK_BASE_RATE * FIELD_GET(RST_MUL_BITS, cfg);

iounmap(base);

error_put_pdev:
pci_dev_put(pdev);

error:
return ret;
}

struct ptp *ptp_get(void)
{
struct pci_dev *pdev;
struct ptp *ptp;

/* If the PTP pci device is found on the system and ptp
* driver is bound to it then the PTP pci device is returned
* to the caller(rvu driver).
*/
pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM,
PCI_DEVID_OCTEONTX2_PTP, NULL);
if (!pdev)
return ERR_PTR(-ENODEV);

ptp = pci_get_drvdata(pdev);
if (!ptp)
ptp = ERR_PTR(-EPROBE_DEFER);
if (IS_ERR(ptp))
pci_dev_put(pdev);

return ptp;
}

void ptp_put(struct ptp *ptp)
{
if (!ptp)
return;

pci_dev_put(ptp->pdev);
}

static int ptp_adjfine(struct ptp *ptp, long scaled_ppm)
{
bool neg_adj = false;
u64 comp;
u64 adj;
s64 ppb;

if (scaled_ppm < 0) {
neg_adj = true;
scaled_ppm = -scaled_ppm;
}

/* The hardware adds the clock compensation value to the PTP clock
* on every coprocessor clock cycle. Typical convention is that it
* represent number of nanosecond betwen each cycle. In this
* convention compensation value is in 64 bit fixed-point
* representation where upper 32 bits are number of nanoseconds
* and lower is fractions of nanosecond.
* The scaled_ppm represent the ratio in "parts per million" by which
* the compensation value should be corrected.
* To calculate new compenstation value we use 64bit fixed point
* arithmetic on following formula
* comp = tbase + tbase * scaled_ppm / (1M * 2^16)
* where tbase is the basic compensation value calculated
* initialy in the probe function.
*/
comp = ((u64)1000000000ull << 32) / ptp->clock_rate;
/* convert scaled_ppm to ppb */
ppb = 1 + scaled_ppm;
ppb *= 125;
ppb >>= 13;
adj = comp * ppb;
adj = div_u64(adj, 1000000000ull);
comp = neg_adj ? comp - adj : comp + adj;

writeq(comp, ptp->reg_base + PTP_CLOCK_COMP);

return 0;
}

static int ptp_get_clock(struct ptp *ptp, u64 *clk)
{
/* Return the current PTP clock */
*clk = readq(ptp->reg_base + PTP_CLOCK_HI);

return 0;
}

static int ptp_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct device *dev = &pdev->dev;
struct ptp *ptp;
u64 clock_comp;
u64 clock_cfg;
int err;

ptp = devm_kzalloc(dev, sizeof(*ptp), GFP_KERNEL);
if (!ptp) {
err = -ENOMEM;
goto error;
}

ptp->pdev = pdev;

err = pcim_enable_device(pdev);
if (err)
goto error_free;

err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev));
if (err)
goto error_free;

ptp->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO];

ptp->clock_rate = get_clock_rate();

/* Enable PTP clock */
clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG);
clock_cfg |= PTP_CLOCK_CFG_PTP_EN;
writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);

clock_comp = ((u64)1000000000ull << 32) / ptp->clock_rate;
/* Initial compensation value to start the nanosecs counter */
writeq(clock_comp, ptp->reg_base + PTP_CLOCK_COMP);

pci_set_drvdata(pdev, ptp);

return 0;

error_free:
devm_kfree(dev, ptp);

error:
/* For `ptp_get()` we need to differentiate between the case
* when the core has not tried to probe this device and the case when
* the probe failed. In the later case we pretend that the
* initialization was successful and keep the error in
* `dev->driver_data`.
*/
pci_set_drvdata(pdev, ERR_PTR(err));
return 0;
}

static void ptp_remove(struct pci_dev *pdev)
{
struct ptp *ptp = pci_get_drvdata(pdev);
u64 clock_cfg;

if (IS_ERR_OR_NULL(ptp))
return;

/* Disable PTP clock */
clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG);
clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN;
writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG);
}

static const struct pci_device_id ptp_id_table[] = {
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
PCI_VENDOR_ID_CAVIUM,
PCI_SUBSYS_DEVID_OCTX2_98xx_PTP) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
PCI_VENDOR_ID_CAVIUM,
PCI_SUBSYS_DEVID_OCTX2_96XX_PTP) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
PCI_VENDOR_ID_CAVIUM,
PCI_SUBSYS_DEVID_OCTX2_95XX_PTP) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
PCI_VENDOR_ID_CAVIUM,
PCI_SUBSYS_DEVID_OCTX2_LOKI_PTP) },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP,
PCI_VENDOR_ID_CAVIUM,
PCI_SUBSYS_DEVID_OCTX2_95MM_PTP) },
{ 0, }
};

struct pci_driver ptp_driver = {
.name = DRV_NAME,
.id_table = ptp_id_table,
.probe = ptp_probe,
.remove = ptp_remove,
};

int rvu_mbox_handler_ptp_op(struct rvu *rvu, struct ptp_req *req,
struct ptp_rsp *rsp)
{
int err = 0;

/* This function is the PTP mailbox handler invoked when
* called by AF consumers/netdev drivers via mailbox mechanism.
* It is used by netdev driver to get the PTP clock and to set
* frequency adjustments. Since mailbox can be called without
* notion of whether the driver is bound to ptp device below
* validation is needed as first step.
*/
if (!rvu->ptp)
return -ENODEV;

switch (req->op) {
case PTP_OP_ADJFINE:
err = ptp_adjfine(rvu->ptp, req->scaled_ppm);
break;
case PTP_OP_GET_CLOCK:
err = ptp_get_clock(rvu->ptp, &rsp->clk);
break;
default:
err = -EINVAL;
break;
}

return err;
}
Loading

0 comments on commit 64d123f

Please sign in to comment.