Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Marvell PPv2 driver.
#
obj-$(CONFIG_MVPP2) := mvpp2.o
mvpp2-y := mvpp2_main.o mvpp2_prs.o mvpp2_cls.o mvpp2_debugfs.o
mvpp2-$(CONFIG_MVPP2_PTP) += mvpp2_tai.o
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,316 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* RSS and Classifier definitions for Marvell PPv2 Network Controller
*
* Copyright (C) 2014 Marvell
*
* Marcin Wojtas <mw@semihalf.com>
*/
#ifndef _MVPP2_CLS_H_
#define _MVPP2_CLS_H_
#include "mvpp2.h"
#include "mvpp2_prs.h"
/* Classifier constants */
#define MVPP2_CLS_FLOWS_TBL_SIZE 512
#define MVPP2_CLS_FLOWS_TBL_DATA_WORDS 3
#define MVPP2_CLS_LKP_TBL_SIZE 64
#define MVPP2_CLS_RX_QUEUES 256
/* Classifier flow constants */
#define MVPP2_FLOW_N_FIELDS 4
enum mvpp2_cls_engine {
MVPP22_CLS_ENGINE_C2 = 1,
MVPP22_CLS_ENGINE_C3A,
MVPP22_CLS_ENGINE_C3B,
MVPP22_CLS_ENGINE_C4,
MVPP22_CLS_ENGINE_C3HA = 6,
MVPP22_CLS_ENGINE_C3HB = 7,
};
#define MVPP22_CLS_HEK_OPT_MAC_DA BIT(0)
#define MVPP22_CLS_HEK_OPT_VLAN_PRI BIT(1)
#define MVPP22_CLS_HEK_OPT_VLAN BIT(2)
#define MVPP22_CLS_HEK_OPT_L3_PROTO BIT(3)
#define MVPP22_CLS_HEK_OPT_IP4SA BIT(4)
#define MVPP22_CLS_HEK_OPT_IP4DA BIT(5)
#define MVPP22_CLS_HEK_OPT_IP6SA BIT(6)
#define MVPP22_CLS_HEK_OPT_IP6DA BIT(7)
#define MVPP22_CLS_HEK_OPT_L4SIP BIT(8)
#define MVPP22_CLS_HEK_OPT_L4DIP BIT(9)
#define MVPP22_CLS_HEK_N_FIELDS 10
#define MVPP22_CLS_HEK_L4_OPTS (MVPP22_CLS_HEK_OPT_L4SIP | \
MVPP22_CLS_HEK_OPT_L4DIP)
#define MVPP22_CLS_HEK_IP4_2T (MVPP22_CLS_HEK_OPT_IP4SA | \
MVPP22_CLS_HEK_OPT_IP4DA)
#define MVPP22_CLS_HEK_IP6_2T (MVPP22_CLS_HEK_OPT_IP6SA | \
MVPP22_CLS_HEK_OPT_IP6DA)
/* The fifth tuple in "5T" is the L4_Info field */
#define MVPP22_CLS_HEK_IP4_5T (MVPP22_CLS_HEK_IP4_2T | \
MVPP22_CLS_HEK_L4_OPTS)
#define MVPP22_CLS_HEK_IP6_5T (MVPP22_CLS_HEK_IP6_2T | \
MVPP22_CLS_HEK_L4_OPTS)
#define MVPP22_CLS_HEK_TAGGED (MVPP22_CLS_HEK_OPT_VLAN | \
MVPP22_CLS_HEK_OPT_VLAN_PRI)
enum mvpp2_cls_field_id {
MVPP22_CLS_FIELD_MAC_DA = 0x03,
MVPP22_CLS_FIELD_VLAN_PRI = 0x05,
MVPP22_CLS_FIELD_VLAN = 0x06,
MVPP22_CLS_FIELD_L3_PROTO = 0x0f,
MVPP22_CLS_FIELD_IP4SA = 0x10,
MVPP22_CLS_FIELD_IP4DA = 0x11,
MVPP22_CLS_FIELD_IP6SA = 0x17,
MVPP22_CLS_FIELD_IP6DA = 0x1a,
MVPP22_CLS_FIELD_L4SIP = 0x1d,
MVPP22_CLS_FIELD_L4DIP = 0x1e,
};
/* Classifier C2 engine constants */
#define MVPP22_CLS_C2_TCAM_EN(data) ((data) << 16)
enum mvpp22_cls_c2_action {
MVPP22_C2_NO_UPD = 0,
MVPP22_C2_NO_UPD_LOCK,
MVPP22_C2_UPD,
MVPP22_C2_UPD_LOCK,
};
enum mvpp22_cls_c2_fwd_action {
MVPP22_C2_FWD_NO_UPD = 0,
MVPP22_C2_FWD_NO_UPD_LOCK,
MVPP22_C2_FWD_SW,
MVPP22_C2_FWD_SW_LOCK,
MVPP22_C2_FWD_HW,
MVPP22_C2_FWD_HW_LOCK,
MVPP22_C2_FWD_HW_LOW_LAT,
MVPP22_C2_FWD_HW_LOW_LAT_LOCK,
};
enum mvpp22_cls_c2_color_action {
MVPP22_C2_COL_NO_UPD = 0,
MVPP22_C2_COL_NO_UPD_LOCK,
MVPP22_C2_COL_GREEN,
MVPP22_C2_COL_GREEN_LOCK,
MVPP22_C2_COL_YELLOW,
MVPP22_C2_COL_YELLOW_LOCK,
MVPP22_C2_COL_RED, /* Drop */
MVPP22_C2_COL_RED_LOCK, /* Drop */
};
#define MVPP2_CLS_C2_TCAM_WORDS 5
#define MVPP2_CLS_C2_ATTR_WORDS 5
struct mvpp2_cls_c2_entry {
u32 index;
/* TCAM lookup key */
u32 tcam[MVPP2_CLS_C2_TCAM_WORDS];
/* Actions to perform upon TCAM match */
u32 act;
/* Attributes relative to the actions to perform */
u32 attr[MVPP2_CLS_C2_ATTR_WORDS];
/* Entry validity */
u8 valid;
};
#define MVPP22_FLOW_ETHER_BIT BIT(0)
#define MVPP22_FLOW_IP4_BIT BIT(1)
#define MVPP22_FLOW_IP6_BIT BIT(2)
#define MVPP22_FLOW_TCP_BIT BIT(3)
#define MVPP22_FLOW_UDP_BIT BIT(4)
#define MVPP22_FLOW_TCP4 (MVPP22_FLOW_ETHER_BIT | MVPP22_FLOW_IP4_BIT | MVPP22_FLOW_TCP_BIT)
#define MVPP22_FLOW_TCP6 (MVPP22_FLOW_ETHER_BIT | MVPP22_FLOW_IP6_BIT | MVPP22_FLOW_TCP_BIT)
#define MVPP22_FLOW_UDP4 (MVPP22_FLOW_ETHER_BIT | MVPP22_FLOW_IP4_BIT | MVPP22_FLOW_UDP_BIT)
#define MVPP22_FLOW_UDP6 (MVPP22_FLOW_ETHER_BIT | MVPP22_FLOW_IP6_BIT | MVPP22_FLOW_UDP_BIT)
#define MVPP22_FLOW_IP4 (MVPP22_FLOW_ETHER_BIT | MVPP22_FLOW_IP4_BIT)
#define MVPP22_FLOW_IP6 (MVPP22_FLOW_ETHER_BIT | MVPP22_FLOW_IP6_BIT)
#define MVPP22_FLOW_ETHERNET (MVPP22_FLOW_ETHER_BIT)
/* Classifier C2 engine entries */
#define MVPP22_CLS_C2_N_ENTRIES 256
/* Number of per-port dedicated entries in the C2 TCAM */
#define MVPP22_CLS_C2_PORT_N_FLOWS MVPP2_N_RFS_ENTRIES_PER_FLOW
/* Each port has one range per flow type + one entry controlling the global RSS
* setting and the default rx queue
*/
#define MVPP22_CLS_C2_PORT_RANGE (MVPP22_CLS_C2_PORT_N_FLOWS + 1)
#define MVPP22_CLS_C2_PORT_FIRST(p) ((p) * MVPP22_CLS_C2_PORT_RANGE)
#define MVPP22_CLS_C2_RSS_ENTRY(p) (MVPP22_CLS_C2_PORT_FIRST((p) + 1) - 1)
#define MVPP22_CLS_C2_PORT_FLOW_FIRST(p) (MVPP22_CLS_C2_PORT_FIRST(p))
#define MVPP22_CLS_C2_RFS_LOC(p, loc) (MVPP22_CLS_C2_PORT_FLOW_FIRST(p) + (loc))
/* Packet flow ID */
enum mvpp2_prs_flow {
MVPP2_FL_START = 8,
MVPP2_FL_IP4_TCP_NF_UNTAG = MVPP2_FL_START,
MVPP2_FL_IP4_UDP_NF_UNTAG,
MVPP2_FL_IP4_TCP_NF_TAG,
MVPP2_FL_IP4_UDP_NF_TAG,
MVPP2_FL_IP6_TCP_NF_UNTAG,
MVPP2_FL_IP6_UDP_NF_UNTAG,
MVPP2_FL_IP6_TCP_NF_TAG,
MVPP2_FL_IP6_UDP_NF_TAG,
MVPP2_FL_IP4_TCP_FRAG_UNTAG,
MVPP2_FL_IP4_UDP_FRAG_UNTAG,
MVPP2_FL_IP4_TCP_FRAG_TAG,
MVPP2_FL_IP4_UDP_FRAG_TAG,
MVPP2_FL_IP6_TCP_FRAG_UNTAG,
MVPP2_FL_IP6_UDP_FRAG_UNTAG,
MVPP2_FL_IP6_TCP_FRAG_TAG,
MVPP2_FL_IP6_UDP_FRAG_TAG,
MVPP2_FL_IP4_UNTAG, /* non-TCP, non-UDP, same for below */
MVPP2_FL_IP4_TAG,
MVPP2_FL_IP6_UNTAG,
MVPP2_FL_IP6_TAG,
MVPP2_FL_NON_IP_UNTAG,
MVPP2_FL_NON_IP_TAG,
MVPP2_FL_LAST,
};
/* LU Type defined for all engines, and specified in the flow table */
#define MVPP2_CLS_LU_TYPE_MASK 0x3f
enum mvpp2_cls_lu_type {
/* rule->loc is used as a lu-type for the entries 0 - 62. */
MVPP22_CLS_LU_TYPE_ALL = 63,
};
#define MVPP2_N_FLOWS (MVPP2_FL_LAST - MVPP2_FL_START)
struct mvpp2_cls_flow {
/* The L2-L4 traffic flow type */
int flow_type;
/* The first id in the flow table for this flow */
u16 flow_id;
/* The supported HEK fields for this flow */
u16 supported_hash_opts;
/* The Header Parser result_info that matches this flow */
struct mvpp2_prs_result_info prs_ri;
};
#define MVPP2_CLS_FLT_ENTRIES_PER_FLOW (MVPP2_MAX_PORTS + 1 + 16)
#define MVPP2_CLS_FLT_FIRST(id) (((id) - MVPP2_FL_START) * \
MVPP2_CLS_FLT_ENTRIES_PER_FLOW)
#define MVPP2_CLS_FLT_C2_RFS(port, id, rfs_n) (MVPP2_CLS_FLT_FIRST(id) + \
((port) * MVPP2_MAX_PORTS) + \
(rfs_n))
#define MVPP2_CLS_FLT_C2_RSS_ENTRY(id) (MVPP2_CLS_FLT_C2_RFS(MVPP2_MAX_PORTS, id, 0))
#define MVPP2_CLS_FLT_HASH_ENTRY(port, id) (MVPP2_CLS_FLT_C2_RSS_ENTRY(id) + 1 + (port))
#define MVPP2_CLS_FLT_LAST(id) (MVPP2_CLS_FLT_FIRST(id) + \
MVPP2_CLS_FLT_ENTRIES_PER_FLOW - 1)
/* Iterate on each classifier flow id. Sets 'i' to be the index of the first
* entry in the cls_flows table for each different flow_id.
* This relies on entries having the same flow_id in the cls_flows table being
* contiguous.
*/
#define for_each_cls_flow_id(i) \
for ((i) = 0; (i) < MVPP2_N_PRS_FLOWS; (i)++) \
if ((i) > 0 && \
cls_flows[(i)].flow_id == cls_flows[(i) - 1].flow_id) \
continue; \
else
/* Iterate on each classifier flow that has a given flow_type. Sets 'i' to be
* the index of the first entry in the cls_flow table for each different flow_id
* that has the given flow_type. This allows to operate on all flows that
* matches a given ethtool flow type.
*/
#define for_each_cls_flow_id_with_type(i, type) \
for_each_cls_flow_id((i)) \
if (cls_flows[(i)].flow_type != (type)) \
continue; \
else
#define for_each_cls_flow_id_containing_type(i, type) \
for_each_cls_flow_id((i)) \
if ((cls_flows[(i)].flow_type & (type)) != (type)) \
continue; \
else
struct mvpp2_cls_flow_entry {
u32 index;
u32 data[MVPP2_CLS_FLOWS_TBL_DATA_WORDS];
};
struct mvpp2_cls_lookup_entry {
u32 lkpid;
u32 way;
u32 data;
};
int mvpp22_port_rss_init(struct mvpp2_port *port);
int mvpp22_port_rss_enable(struct mvpp2_port *port);
int mvpp22_port_rss_disable(struct mvpp2_port *port);
int mvpp22_port_rss_ctx_create(struct mvpp2_port *port, u32 rss_ctx);
int mvpp22_port_rss_ctx_delete(struct mvpp2_port *port, u32 rss_ctx);
int mvpp22_port_rss_ctx_indir_set(struct mvpp2_port *port, u32 rss_ctx,
const u32 *indir);
int mvpp22_port_rss_ctx_indir_get(struct mvpp2_port *port, u32 rss_ctx,
u32 *indir);
int mvpp2_ethtool_rxfh_get(struct mvpp2_port *port,
struct ethtool_rxfh_fields *info);
int mvpp2_ethtool_rxfh_set(struct mvpp2_port *port,
const struct ethtool_rxfh_fields *info);
void mvpp2_cls_init(struct mvpp2 *priv);
void mvpp2_cls_port_config(struct mvpp2_port *port);
void mvpp2_cls_oversize_rxq_set(struct mvpp2_port *port);
int mvpp2_cls_flow_eng_get(struct mvpp2_cls_flow_entry *fe);
u16 mvpp2_flow_get_hek_fields(struct mvpp2_cls_flow_entry *fe);
const struct mvpp2_cls_flow *mvpp2_cls_flow_get(int flow);
u32 mvpp2_cls_flow_hits(struct mvpp2 *priv, int index);
void mvpp2_cls_flow_read(struct mvpp2 *priv, int index,
struct mvpp2_cls_flow_entry *fe);
u32 mvpp2_cls_lookup_hits(struct mvpp2 *priv, int index);
void mvpp2_cls_lookup_read(struct mvpp2 *priv, int lkpid, int way,
struct mvpp2_cls_lookup_entry *le);
u32 mvpp2_cls_c2_hit_count(struct mvpp2 *priv, int c2_index);
void mvpp2_cls_c2_read(struct mvpp2 *priv, int index,
struct mvpp2_cls_c2_entry *c2);
int mvpp2_ethtool_cls_rule_get(struct mvpp2_port *port,
struct ethtool_rxnfc *rxnfc);
int mvpp2_ethtool_cls_rule_ins(struct mvpp2_port *port,
struct ethtool_rxnfc *info);
int mvpp2_ethtool_cls_rule_del(struct mvpp2_port *port,
struct ethtool_rxnfc *info);
#endif
@@ -0,0 +1,734 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for Marvell PPv2 network controller for Armada 375 SoC.
*
* Copyright (C) 2018 Marvell
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/debugfs.h>
#include "mvpp2.h"
#include "mvpp2_prs.h"
#include "mvpp2_cls.h"
struct mvpp2_dbgfs_prs_entry {
int tid;
struct mvpp2 *priv;
};
struct mvpp2_dbgfs_c2_entry {
int id;
struct mvpp2 *priv;
};
struct mvpp2_dbgfs_flow_entry {
int flow;
struct mvpp2 *priv;
};
struct mvpp2_dbgfs_flow_tbl_entry {
int id;
struct mvpp2 *priv;
};
struct mvpp2_dbgfs_port_flow_entry {
struct mvpp2_port *port;
struct mvpp2_dbgfs_flow_entry *dbg_fe;
};
struct mvpp2_dbgfs_entries {
/* Entries for Header Parser debug info */
struct mvpp2_dbgfs_prs_entry prs_entries[MVPP2_PRS_TCAM_SRAM_SIZE];
/* Entries for Classifier C2 engine debug info */
struct mvpp2_dbgfs_c2_entry c2_entries[MVPP22_CLS_C2_N_ENTRIES];
/* Entries for Classifier Flow Table debug info */
struct mvpp2_dbgfs_flow_tbl_entry flt_entries[MVPP2_CLS_FLOWS_TBL_SIZE];
/* Entries for Classifier flows debug info */
struct mvpp2_dbgfs_flow_entry flow_entries[MVPP2_N_PRS_FLOWS];
/* Entries for per-port flows debug info */
struct mvpp2_dbgfs_port_flow_entry port_flow_entries[MVPP2_MAX_PORTS];
};
static int mvpp2_dbgfs_flow_flt_hits_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_flow_tbl_entry *entry = s->private;
u32 hits = mvpp2_cls_flow_hits(entry->priv, entry->id);
seq_printf(s, "%u\n", hits);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_flt_hits);
static int mvpp2_dbgfs_flow_dec_hits_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_flow_entry *entry = s->private;
u32 hits = mvpp2_cls_lookup_hits(entry->priv, entry->flow);
seq_printf(s, "%u\n", hits);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_dec_hits);
static int mvpp2_dbgfs_flow_type_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_flow_entry *entry = s->private;
const struct mvpp2_cls_flow *f;
const char *flow_name;
f = mvpp2_cls_flow_get(entry->flow);
if (!f)
return -EINVAL;
switch (f->flow_type) {
case IPV4_FLOW:
flow_name = "ipv4";
break;
case IPV6_FLOW:
flow_name = "ipv6";
break;
case TCP_V4_FLOW:
flow_name = "tcp4";
break;
case TCP_V6_FLOW:
flow_name = "tcp6";
break;
case UDP_V4_FLOW:
flow_name = "udp4";
break;
case UDP_V6_FLOW:
flow_name = "udp6";
break;
default:
flow_name = "other";
}
seq_printf(s, "%s\n", flow_name);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_type);
static int mvpp2_dbgfs_flow_id_show(struct seq_file *s, void *unused)
{
const struct mvpp2_dbgfs_flow_entry *entry = s->private;
const struct mvpp2_cls_flow *f;
f = mvpp2_cls_flow_get(entry->flow);
if (!f)
return -EINVAL;
seq_printf(s, "%d\n", f->flow_id);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_id);
static int mvpp2_dbgfs_port_flow_hash_opt_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_port_flow_entry *entry = s->private;
struct mvpp2_port *port = entry->port;
struct mvpp2_cls_flow_entry fe;
const struct mvpp2_cls_flow *f;
int flow_index;
u16 hash_opts;
f = mvpp2_cls_flow_get(entry->dbg_fe->flow);
if (!f)
return -EINVAL;
flow_index = MVPP2_CLS_FLT_HASH_ENTRY(entry->port->id, f->flow_id);
mvpp2_cls_flow_read(port->priv, flow_index, &fe);
hash_opts = mvpp2_flow_get_hek_fields(&fe);
seq_printf(s, "0x%04x\n", hash_opts);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_flow_hash_opt);
static int mvpp2_dbgfs_port_flow_engine_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_port_flow_entry *entry = s->private;
struct mvpp2_port *port = entry->port;
struct mvpp2_cls_flow_entry fe;
const struct mvpp2_cls_flow *f;
int flow_index, engine;
f = mvpp2_cls_flow_get(entry->dbg_fe->flow);
if (!f)
return -EINVAL;
flow_index = MVPP2_CLS_FLT_HASH_ENTRY(entry->port->id, f->flow_id);
mvpp2_cls_flow_read(port->priv, flow_index, &fe);
engine = mvpp2_cls_flow_eng_get(&fe);
seq_printf(s, "%d\n", engine);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_flow_engine);
static int mvpp2_dbgfs_flow_c2_hits_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_c2_entry *entry = s->private;
u32 hits;
hits = mvpp2_cls_c2_hit_count(entry->priv, entry->id);
seq_printf(s, "%u\n", hits);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_c2_hits);
static int mvpp2_dbgfs_flow_c2_rxq_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_c2_entry *entry = s->private;
struct mvpp2_cls_c2_entry c2;
u8 qh, ql;
mvpp2_cls_c2_read(entry->priv, entry->id, &c2);
qh = (c2.attr[0] >> MVPP22_CLS_C2_ATTR0_QHIGH_OFFS) &
MVPP22_CLS_C2_ATTR0_QHIGH_MASK;
ql = (c2.attr[0] >> MVPP22_CLS_C2_ATTR0_QLOW_OFFS) &
MVPP22_CLS_C2_ATTR0_QLOW_MASK;
seq_printf(s, "%d\n", (qh << 3 | ql));
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_c2_rxq);
static int mvpp2_dbgfs_flow_c2_enable_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_c2_entry *entry = s->private;
struct mvpp2_cls_c2_entry c2;
int enabled;
mvpp2_cls_c2_read(entry->priv, entry->id, &c2);
enabled = !!(c2.attr[2] & MVPP22_CLS_C2_ATTR2_RSS_EN);
seq_printf(s, "%d\n", enabled);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_flow_c2_enable);
static int mvpp2_dbgfs_port_vid_show(struct seq_file *s, void *unused)
{
struct mvpp2_port *port = s->private;
unsigned char byte[2], enable[2];
struct mvpp2 *priv = port->priv;
struct mvpp2_prs_entry pe;
unsigned long pmap;
u16 rvid;
int tid;
for (tid = MVPP2_PRS_VID_PORT_FIRST(port->id);
tid <= MVPP2_PRS_VID_PORT_LAST(port->id); tid++) {
mvpp2_prs_init_from_hw(priv, &pe, tid);
pmap = mvpp2_prs_tcam_port_map_get(&pe);
if (!priv->prs_shadow[tid].valid)
continue;
if (!test_bit(port->id, &pmap))
continue;
mvpp2_prs_tcam_data_byte_get(&pe, 2, &byte[0], &enable[0]);
mvpp2_prs_tcam_data_byte_get(&pe, 3, &byte[1], &enable[1]);
rvid = ((byte[0] & 0xf) << 8) + byte[1];
seq_printf(s, "%u\n", rvid);
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_vid);
static int mvpp2_dbgfs_port_parser_show(struct seq_file *s, void *unused)
{
struct mvpp2_port *port = s->private;
struct mvpp2 *priv = port->priv;
struct mvpp2_prs_entry pe;
unsigned long pmap;
int i;
for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) {
mvpp2_prs_init_from_hw(port->priv, &pe, i);
pmap = mvpp2_prs_tcam_port_map_get(&pe);
if (priv->prs_shadow[i].valid && test_bit(port->id, &pmap))
seq_printf(s, "%03d\n", i);
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_port_parser);
static int mvpp2_dbgfs_filter_show(struct seq_file *s, void *unused)
{
struct mvpp2_port *port = s->private;
struct mvpp2 *priv = port->priv;
struct mvpp2_prs_entry pe;
unsigned long pmap;
int index, tid;
for (tid = MVPP2_PE_MAC_RANGE_START;
tid <= MVPP2_PE_MAC_RANGE_END; tid++) {
unsigned char da[ETH_ALEN], da_mask[ETH_ALEN];
if (!priv->prs_shadow[tid].valid ||
priv->prs_shadow[tid].lu != MVPP2_PRS_LU_MAC ||
priv->prs_shadow[tid].udf != MVPP2_PRS_UDF_MAC_DEF)
continue;
mvpp2_prs_init_from_hw(priv, &pe, tid);
pmap = mvpp2_prs_tcam_port_map_get(&pe);
/* We only want entries active on this port */
if (!test_bit(port->id, &pmap))
continue;
/* Read mac addr from entry */
for (index = 0; index < ETH_ALEN; index++)
mvpp2_prs_tcam_data_byte_get(&pe, index, &da[index],
&da_mask[index]);
seq_printf(s, "%pM\n", da);
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_filter);
static int mvpp2_dbgfs_prs_lu_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_prs_entry *entry = s->private;
struct mvpp2 *priv = entry->priv;
seq_printf(s, "%x\n", priv->prs_shadow[entry->tid].lu);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_lu);
static int mvpp2_dbgfs_prs_pmap_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_prs_entry *entry = s->private;
struct mvpp2_prs_entry pe;
unsigned int pmap;
mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
pmap = mvpp2_prs_tcam_port_map_get(&pe);
pmap &= MVPP2_PRS_PORT_MASK;
seq_printf(s, "%02x\n", pmap);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_pmap);
static int mvpp2_dbgfs_prs_ai_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_prs_entry *entry = s->private;
struct mvpp2_prs_entry pe;
unsigned char ai, ai_mask;
mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
ai = pe.tcam[MVPP2_PRS_TCAM_AI_WORD] & MVPP2_PRS_AI_MASK;
ai_mask = (pe.tcam[MVPP2_PRS_TCAM_AI_WORD] >> 16) & MVPP2_PRS_AI_MASK;
seq_printf(s, "%02x %02x\n", ai, ai_mask);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_ai);
static int mvpp2_dbgfs_prs_hdata_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_prs_entry *entry = s->private;
struct mvpp2_prs_entry pe;
unsigned char data[8], mask[8];
int i;
mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
for (i = 0; i < 8; i++)
mvpp2_prs_tcam_data_byte_get(&pe, i, &data[i], &mask[i]);
seq_printf(s, "%*phN %*phN\n", 8, data, 8, mask);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_hdata);
static int mvpp2_dbgfs_prs_sram_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_prs_entry *entry = s->private;
struct mvpp2_prs_entry pe;
mvpp2_prs_init_from_hw(entry->priv, &pe, entry->tid);
seq_printf(s, "%*phN\n", 14, pe.sram);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_sram);
static int mvpp2_dbgfs_prs_hits_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_prs_entry *entry = s->private;
int val;
val = mvpp2_prs_hits(entry->priv, entry->tid);
if (val < 0)
return val;
seq_printf(s, "%d\n", val);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_hits);
static int mvpp2_dbgfs_prs_valid_show(struct seq_file *s, void *unused)
{
struct mvpp2_dbgfs_prs_entry *entry = s->private;
struct mvpp2 *priv = entry->priv;
int tid = entry->tid;
seq_printf(s, "%d\n", priv->prs_shadow[tid].valid ? 1 : 0);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(mvpp2_dbgfs_prs_valid);
static int mvpp2_dbgfs_flow_port_init(struct dentry *parent,
struct mvpp2_port *port,
struct mvpp2_dbgfs_flow_entry *entry)
{
struct mvpp2_dbgfs_port_flow_entry *port_entry;
struct dentry *port_dir;
port_dir = debugfs_create_dir(port->dev->name, parent);
port_entry = &port->priv->dbgfs_entries->port_flow_entries[port->id];
port_entry->port = port;
port_entry->dbg_fe = entry;
debugfs_create_file("hash_opts", 0444, port_dir, port_entry,
&mvpp2_dbgfs_port_flow_hash_opt_fops);
debugfs_create_file("engine", 0444, port_dir, port_entry,
&mvpp2_dbgfs_port_flow_engine_fops);
return 0;
}
static int mvpp2_dbgfs_flow_entry_init(struct dentry *parent,
struct mvpp2 *priv, int flow)
{
struct mvpp2_dbgfs_flow_entry *entry;
struct dentry *flow_entry_dir;
char flow_entry_name[10];
int i, ret;
sprintf(flow_entry_name, "%02d", flow);
flow_entry_dir = debugfs_create_dir(flow_entry_name, parent);
entry = &priv->dbgfs_entries->flow_entries[flow];
entry->flow = flow;
entry->priv = priv;
debugfs_create_file("dec_hits", 0444, flow_entry_dir, entry,
&mvpp2_dbgfs_flow_dec_hits_fops);
debugfs_create_file("type", 0444, flow_entry_dir, entry,
&mvpp2_dbgfs_flow_type_fops);
debugfs_create_file("id", 0444, flow_entry_dir, entry,
&mvpp2_dbgfs_flow_id_fops);
/* Create entry for each port */
for (i = 0; i < priv->port_count; i++) {
ret = mvpp2_dbgfs_flow_port_init(flow_entry_dir,
priv->port_list[i], entry);
if (ret)
return ret;
}
return 0;
}
static int mvpp2_dbgfs_flow_init(struct dentry *parent, struct mvpp2 *priv)
{
struct dentry *flow_dir;
int i, ret;
flow_dir = debugfs_create_dir("flows", parent);
for (i = 0; i < MVPP2_N_PRS_FLOWS; i++) {
ret = mvpp2_dbgfs_flow_entry_init(flow_dir, priv, i);
if (ret)
return ret;
}
return 0;
}
static int mvpp2_dbgfs_prs_entry_init(struct dentry *parent,
struct mvpp2 *priv, int tid)
{
struct mvpp2_dbgfs_prs_entry *entry;
struct dentry *prs_entry_dir;
char prs_entry_name[10];
if (tid >= MVPP2_PRS_TCAM_SRAM_SIZE)
return -EINVAL;
sprintf(prs_entry_name, "%03d", tid);
prs_entry_dir = debugfs_create_dir(prs_entry_name, parent);
entry = &priv->dbgfs_entries->prs_entries[tid];
entry->tid = tid;
entry->priv = priv;
/* Create each attr */
debugfs_create_file("sram", 0444, prs_entry_dir, entry,
&mvpp2_dbgfs_prs_sram_fops);
debugfs_create_file("valid", 0644, prs_entry_dir, entry,
&mvpp2_dbgfs_prs_valid_fops);
debugfs_create_file("lookup_id", 0644, prs_entry_dir, entry,
&mvpp2_dbgfs_prs_lu_fops);
debugfs_create_file("ai", 0644, prs_entry_dir, entry,
&mvpp2_dbgfs_prs_ai_fops);
debugfs_create_file("header_data", 0644, prs_entry_dir, entry,
&mvpp2_dbgfs_prs_hdata_fops);
debugfs_create_file("hits", 0444, prs_entry_dir, entry,
&mvpp2_dbgfs_prs_hits_fops);
debugfs_create_file("pmap", 0444, prs_entry_dir, entry,
&mvpp2_dbgfs_prs_pmap_fops);
return 0;
}
static int mvpp2_dbgfs_prs_init(struct dentry *parent, struct mvpp2 *priv)
{
struct dentry *prs_dir;
int i, ret;
prs_dir = debugfs_create_dir("parser", parent);
for (i = 0; i < MVPP2_PRS_TCAM_SRAM_SIZE; i++) {
ret = mvpp2_dbgfs_prs_entry_init(prs_dir, priv, i);
if (ret)
return ret;
}
return 0;
}
static int mvpp2_dbgfs_c2_entry_init(struct dentry *parent,
struct mvpp2 *priv, int id)
{
struct mvpp2_dbgfs_c2_entry *entry;
struct dentry *c2_entry_dir;
char c2_entry_name[10];
if (id >= MVPP22_CLS_C2_N_ENTRIES)
return -EINVAL;
sprintf(c2_entry_name, "%03d", id);
c2_entry_dir = debugfs_create_dir(c2_entry_name, parent);
entry = &priv->dbgfs_entries->c2_entries[id];
entry->id = id;
entry->priv = priv;
debugfs_create_file("hits", 0444, c2_entry_dir, entry,
&mvpp2_dbgfs_flow_c2_hits_fops);
debugfs_create_file("default_rxq", 0444, c2_entry_dir, entry,
&mvpp2_dbgfs_flow_c2_rxq_fops);
debugfs_create_file("rss_enable", 0444, c2_entry_dir, entry,
&mvpp2_dbgfs_flow_c2_enable_fops);
return 0;
}
static int mvpp2_dbgfs_flow_tbl_entry_init(struct dentry *parent,
struct mvpp2 *priv, int id)
{
struct mvpp2_dbgfs_flow_tbl_entry *entry;
struct dentry *flow_tbl_entry_dir;
char flow_tbl_entry_name[10];
if (id >= MVPP2_CLS_FLOWS_TBL_SIZE)
return -EINVAL;
sprintf(flow_tbl_entry_name, "%03d", id);
flow_tbl_entry_dir = debugfs_create_dir(flow_tbl_entry_name, parent);
entry = &priv->dbgfs_entries->flt_entries[id];
entry->id = id;
entry->priv = priv;
debugfs_create_file("hits", 0444, flow_tbl_entry_dir, entry,
&mvpp2_dbgfs_flow_flt_hits_fops);
return 0;
}
static int mvpp2_dbgfs_cls_init(struct dentry *parent, struct mvpp2 *priv)
{
struct dentry *cls_dir, *c2_dir, *flow_tbl_dir;
int i, ret;
cls_dir = debugfs_create_dir("classifier", parent);
c2_dir = debugfs_create_dir("c2", cls_dir);
for (i = 0; i < MVPP22_CLS_C2_N_ENTRIES; i++) {
ret = mvpp2_dbgfs_c2_entry_init(c2_dir, priv, i);
if (ret)
return ret;
}
flow_tbl_dir = debugfs_create_dir("flow_table", cls_dir);
for (i = 0; i < MVPP2_CLS_FLOWS_TBL_SIZE; i++) {
ret = mvpp2_dbgfs_flow_tbl_entry_init(flow_tbl_dir, priv, i);
if (ret)
return ret;
}
return 0;
}
static int mvpp2_dbgfs_port_init(struct dentry *parent,
struct mvpp2_port *port)
{
struct dentry *port_dir;
port_dir = debugfs_create_dir(port->dev->name, parent);
debugfs_create_file("parser_entries", 0444, port_dir, port,
&mvpp2_dbgfs_port_parser_fops);
debugfs_create_file("mac_filter", 0444, port_dir, port,
&mvpp2_dbgfs_filter_fops);
debugfs_create_file("vid_filter", 0444, port_dir, port,
&mvpp2_dbgfs_port_vid_fops);
return 0;
}
static struct dentry *mvpp2_root;
void mvpp2_dbgfs_exit(void)
{
debugfs_remove(mvpp2_root);
}
void mvpp2_dbgfs_cleanup(struct mvpp2 *priv)
{
debugfs_remove_recursive(priv->dbgfs_dir);
kfree(priv->dbgfs_entries);
}
void mvpp2_dbgfs_init(struct mvpp2 *priv, const char *name)
{
struct dentry *mvpp2_dir;
int ret, i;
if (!mvpp2_root)
mvpp2_root = debugfs_create_dir(MVPP2_DRIVER_NAME, NULL);
mvpp2_dir = debugfs_create_dir(name, mvpp2_root);
priv->dbgfs_dir = mvpp2_dir;
priv->dbgfs_entries = kzalloc_obj(*priv->dbgfs_entries);
if (!priv->dbgfs_entries)
goto err;
ret = mvpp2_dbgfs_prs_init(mvpp2_dir, priv);
if (ret)
goto err;
ret = mvpp2_dbgfs_cls_init(mvpp2_dir, priv);
if (ret)
goto err;
for (i = 0; i < priv->port_count; i++) {
ret = mvpp2_dbgfs_port_init(mvpp2_dir, priv->port_list[i]);
if (ret)
goto err;
}
ret = mvpp2_dbgfs_flow_init(mvpp2_dir, priv);
if (ret)
goto err;
return;
err:
mvpp2_dbgfs_cleanup(priv);
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,335 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Header Parser definitions for Marvell PPv2 Network Controller
*
* Copyright (C) 2014 Marvell
*
* Marcin Wojtas <mw@semihalf.com>
*/
#ifndef _MVPP2_PRS_H_
#define _MVPP2_PRS_H_
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/platform_device.h>
#include "mvpp2.h"
/* Parser constants */
#define MVPP2_PRS_TCAM_SRAM_SIZE 256
#define MVPP2_PRS_TCAM_WORDS 6
#define MVPP2_PRS_SRAM_WORDS 4
#define MVPP2_PRS_FLOW_ID_SIZE 64
#define MVPP2_PRS_FLOW_ID_MASK 0x3f
#define MVPP2_PRS_TCAM_ENTRY_INVALID 1
#define MVPP2_PRS_TCAM_DSA_TAGGED_BIT BIT(5)
#define MVPP2_PRS_IPV4_HEAD 0x40
#define MVPP2_PRS_IPV4_HEAD_MASK 0xf0
#define MVPP2_PRS_IPV4_MC 0xe0
#define MVPP2_PRS_IPV4_MC_MASK 0xf0
#define MVPP2_PRS_IPV4_BC_MASK 0xff
#define MVPP2_PRS_IPV4_IHL_MIN 0x5
#define MVPP2_PRS_IPV4_IHL_MAX 0xf
#define MVPP2_PRS_IPV4_IHL_MASK 0xf
#define MVPP2_PRS_IPV6_MC 0xff
#define MVPP2_PRS_IPV6_MC_MASK 0xff
#define MVPP2_PRS_IPV6_HOP_MASK 0xff
#define MVPP2_PRS_TCAM_PROTO_MASK 0xff
#define MVPP2_PRS_TCAM_PROTO_MASK_L 0x3f
#define MVPP2_PRS_DBL_VLANS_MAX 100
#define MVPP2_PRS_CAST_MASK BIT(0)
#define MVPP2_PRS_MCAST_VAL BIT(0)
#define MVPP2_PRS_UCAST_VAL 0x0
/* Tcam structure:
* - lookup ID - 4 bits
* - port ID - 1 byte
* - additional information - 1 byte
* - header data - 8 bytes
* The fields are represented by MVPP2_PRS_TCAM_DATA_REG(5)->(0).
*/
#define MVPP2_PRS_AI_BITS 8
#define MVPP2_PRS_AI_MASK 0xff
#define MVPP2_PRS_PORT_MASK 0xff
#define MVPP2_PRS_LU_MASK 0xf
/* TCAM entries in registers are accessed using 16 data bits + 16 enable bits */
#define MVPP2_PRS_BYTE_TO_WORD(byte) ((byte) / 2)
#define MVPP2_PRS_BYTE_IN_WORD(byte) ((byte) % 2)
#define MVPP2_PRS_TCAM_EN(data) ((data) << 16)
#define MVPP2_PRS_TCAM_AI_WORD 4
#define MVPP2_PRS_TCAM_AI(ai) (ai)
#define MVPP2_PRS_TCAM_AI_EN(ai) MVPP2_PRS_TCAM_EN(MVPP2_PRS_TCAM_AI(ai))
#define MVPP2_PRS_TCAM_PORT_WORD 4
#define MVPP2_PRS_TCAM_PORT(p) ((p) << 8)
#define MVPP2_PRS_TCAM_PORT_EN(p) MVPP2_PRS_TCAM_EN(MVPP2_PRS_TCAM_PORT(p))
#define MVPP2_PRS_TCAM_LU_WORD 5
#define MVPP2_PRS_TCAM_LU(lu) (lu)
#define MVPP2_PRS_TCAM_LU_EN(lu) MVPP2_PRS_TCAM_EN(MVPP2_PRS_TCAM_LU(lu))
#define MVPP2_PRS_TCAM_INV_WORD 5
#define MVPP2_PRS_VID_TCAM_BYTE 2
/* TCAM range for unicast and multicast filtering. We have 25 entries per port,
* with 4 dedicated to UC filtering and the rest to multicast filtering.
* Additionnally we reserve one entry for the broadcast address, and one for
* each port's own address.
*/
#define MVPP2_PRS_MAC_UC_MC_FILT_MAX 25
#define MVPP2_PRS_MAC_RANGE_SIZE 80
/* Number of entries per port dedicated to UC and MC filtering */
#define MVPP2_PRS_MAC_UC_FILT_MAX 4
#define MVPP2_PRS_MAC_MC_FILT_MAX (MVPP2_PRS_MAC_UC_MC_FILT_MAX - \
MVPP2_PRS_MAC_UC_FILT_MAX)
/* There is a TCAM range reserved for VLAN filtering entries, range size is 33
* 10 VLAN ID filter entries per port
* 1 default VLAN filter entry per port
* It is assumed that there are 3 ports for filter, not including loopback port
*/
#define MVPP2_PRS_VLAN_FILT_MAX 11
#define MVPP2_PRS_VLAN_FILT_RANGE_SIZE 33
#define MVPP2_PRS_VLAN_FILT_MAX_ENTRY (MVPP2_PRS_VLAN_FILT_MAX - 2)
#define MVPP2_PRS_VLAN_FILT_DFLT_ENTRY (MVPP2_PRS_VLAN_FILT_MAX - 1)
/* Tcam entries ID */
#define MVPP2_PE_DROP_ALL 0
#define MVPP2_PE_FIRST_FREE_TID 1
/* MAC filtering range */
#define MVPP2_PE_MAC_RANGE_END (MVPP2_PE_VID_FILT_RANGE_START - 1)
#define MVPP2_PE_MAC_RANGE_START (MVPP2_PE_MAC_RANGE_END - \
MVPP2_PRS_MAC_RANGE_SIZE + 1)
/* VLAN filtering range */
#define MVPP2_PE_VID_FILT_RANGE_END (MVPP2_PRS_TCAM_SRAM_SIZE - 32)
#define MVPP2_PE_VID_FILT_RANGE_START (MVPP2_PE_VID_FILT_RANGE_END - \
MVPP2_PRS_VLAN_FILT_RANGE_SIZE + 1)
#define MVPP2_PE_LAST_FREE_TID (MVPP2_PE_MAC_RANGE_START - 1)
#define MVPP2_PE_MH_SKIP_PRS (MVPP2_PRS_TCAM_SRAM_SIZE - 31)
#define MVPP2_PE_IP6_EXT_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 30)
#define MVPP2_PE_IP6_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 29)
#define MVPP2_PE_IP4_ADDR_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 28)
#define MVPP2_PE_LAST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 27)
#define MVPP2_PE_FIRST_DEFAULT_FLOW (MVPP2_PRS_TCAM_SRAM_SIZE - 22)
#define MVPP2_PE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 21)
#define MVPP2_PE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 20)
#define MVPP2_PE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 19)
#define MVPP2_PE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 18)
#define MVPP2_PE_ETYPE_EDSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 17)
#define MVPP2_PE_ETYPE_EDSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 16)
#define MVPP2_PE_ETYPE_DSA_TAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 15)
#define MVPP2_PE_ETYPE_DSA_UNTAGGED (MVPP2_PRS_TCAM_SRAM_SIZE - 14)
#define MVPP2_PE_MH_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 13)
#define MVPP2_PE_DSA_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 12)
#define MVPP2_PE_IP6_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 11)
#define MVPP2_PE_IP4_PROTO_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 10)
#define MVPP2_PE_ETH_TYPE_UN (MVPP2_PRS_TCAM_SRAM_SIZE - 9)
#define MVPP2_PE_VID_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 8)
#define MVPP2_PE_VID_EDSA_FLTR_DEFAULT (MVPP2_PRS_TCAM_SRAM_SIZE - 7)
#define MVPP2_PE_VLAN_DBL (MVPP2_PRS_TCAM_SRAM_SIZE - 6)
#define MVPP2_PE_VLAN_NONE (MVPP2_PRS_TCAM_SRAM_SIZE - 5)
#define MVPP2_PE_FC_DROP (MVPP2_PRS_TCAM_SRAM_SIZE - 4)
#define MVPP2_PE_MAC_MC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 3)
#define MVPP2_PE_MAC_UC_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 2)
#define MVPP2_PE_MAC_NON_PROMISCUOUS (MVPP2_PRS_TCAM_SRAM_SIZE - 1)
#define MVPP2_PRS_VID_PORT_FIRST(port) (MVPP2_PE_VID_FILT_RANGE_START + \
((port) * MVPP2_PRS_VLAN_FILT_MAX))
#define MVPP2_PRS_VID_PORT_LAST(port) (MVPP2_PRS_VID_PORT_FIRST(port) \
+ MVPP2_PRS_VLAN_FILT_MAX_ENTRY)
/* Index of default vid filter for given port */
#define MVPP2_PRS_VID_PORT_DFLT(port) (MVPP2_PRS_VID_PORT_FIRST(port) \
+ MVPP2_PRS_VLAN_FILT_DFLT_ENTRY)
/* Sram structure
* The fields are represented by MVPP2_PRS_TCAM_DATA_REG(3)->(0).
*/
#define MVPP2_PRS_SRAM_RI_OFFS 0
#define MVPP2_PRS_SRAM_RI_WORD 0
#define MVPP2_PRS_SRAM_RI_CTRL_OFFS 32
#define MVPP2_PRS_SRAM_RI_CTRL_WORD 1
#define MVPP2_PRS_SRAM_RI_CTRL_BITS 32
#define MVPP2_PRS_SRAM_SHIFT_OFFS 64
#define MVPP2_PRS_SRAM_SHIFT_SIGN_BIT 72
#define MVPP2_PRS_SRAM_SHIFT_MASK 0xff
#define MVPP2_PRS_SRAM_UDF_OFFS 73
#define MVPP2_PRS_SRAM_UDF_BITS 8
#define MVPP2_PRS_SRAM_UDF_MASK 0xff
#define MVPP2_PRS_SRAM_UDF_SIGN_BIT 81
#define MVPP2_PRS_SRAM_UDF_TYPE_OFFS 82
#define MVPP2_PRS_SRAM_UDF_TYPE_MASK 0x7
#define MVPP2_PRS_SRAM_UDF_TYPE_L3 1
#define MVPP2_PRS_SRAM_UDF_TYPE_L4 4
#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_OFFS 85
#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_MASK 0x3
#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_ADD 1
#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_IP4_ADD 2
#define MVPP2_PRS_SRAM_OP_SEL_SHIFT_IP6_ADD 3
#define MVPP2_PRS_SRAM_OP_SEL_UDF_OFFS 87
#define MVPP2_PRS_SRAM_OP_SEL_UDF_BITS 2
#define MVPP2_PRS_SRAM_OP_SEL_UDF_MASK 0x3
#define MVPP2_PRS_SRAM_OP_SEL_UDF_ADD 0
#define MVPP2_PRS_SRAM_OP_SEL_UDF_IP4_ADD 2
#define MVPP2_PRS_SRAM_OP_SEL_UDF_IP6_ADD 3
#define MVPP2_PRS_SRAM_OP_SEL_BASE_OFFS 89
#define MVPP2_PRS_SRAM_AI_OFFS 90
#define MVPP2_PRS_SRAM_AI_CTRL_OFFS 98
#define MVPP2_PRS_SRAM_AI_CTRL_BITS 8
#define MVPP2_PRS_SRAM_AI_MASK 0xff
#define MVPP2_PRS_SRAM_NEXT_LU_OFFS 106
#define MVPP2_PRS_SRAM_NEXT_LU_MASK 0xf
#define MVPP2_PRS_SRAM_LU_DONE_BIT 110
#define MVPP2_PRS_SRAM_LU_GEN_BIT 111
/* Sram result info bits assignment */
#define MVPP2_PRS_RI_MAC_ME_MASK 0x1
#define MVPP2_PRS_RI_DSA_MASK 0x2
#define MVPP2_PRS_RI_VLAN_MASK (BIT(2) | BIT(3))
#define MVPP2_PRS_RI_VLAN_NONE 0x0
#define MVPP2_PRS_RI_VLAN_SINGLE BIT(2)
#define MVPP2_PRS_RI_VLAN_DOUBLE BIT(3)
#define MVPP2_PRS_RI_VLAN_TRIPLE (BIT(2) | BIT(3))
#define MVPP2_PRS_RI_CPU_CODE_MASK 0x70
#define MVPP2_PRS_RI_CPU_CODE_RX_SPEC BIT(4)
#define MVPP2_PRS_RI_L2_CAST_MASK (BIT(9) | BIT(10))
#define MVPP2_PRS_RI_L2_UCAST 0x0
#define MVPP2_PRS_RI_L2_MCAST BIT(9)
#define MVPP2_PRS_RI_L2_BCAST BIT(10)
#define MVPP2_PRS_RI_PPPOE_MASK 0x800
#define MVPP2_PRS_RI_L3_PROTO_MASK (BIT(12) | BIT(13) | BIT(14))
#define MVPP2_PRS_RI_L3_UN 0x0
#define MVPP2_PRS_RI_L3_IP4 BIT(12)
#define MVPP2_PRS_RI_L3_IP4_OPT BIT(13)
#define MVPP2_PRS_RI_L3_IP4_OTHER (BIT(12) | BIT(13))
#define MVPP2_PRS_RI_L3_IP6 BIT(14)
#define MVPP2_PRS_RI_L3_IP6_EXT (BIT(12) | BIT(14))
#define MVPP2_PRS_RI_L3_ARP (BIT(13) | BIT(14))
#define MVPP2_PRS_RI_L3_ADDR_MASK (BIT(15) | BIT(16))
#define MVPP2_PRS_RI_L3_UCAST 0x0
#define MVPP2_PRS_RI_L3_MCAST BIT(15)
#define MVPP2_PRS_RI_L3_BCAST (BIT(15) | BIT(16))
#define MVPP2_PRS_RI_IP_FRAG_MASK 0x20000
#define MVPP2_PRS_RI_IP_FRAG_TRUE BIT(17)
#define MVPP2_PRS_RI_UDF3_MASK 0x300000
#define MVPP2_PRS_RI_UDF3_RX_SPECIAL BIT(21)
#define MVPP2_PRS_RI_L4_PROTO_MASK 0x1c00000
#define MVPP2_PRS_RI_L4_TCP BIT(22)
#define MVPP2_PRS_RI_L4_UDP BIT(23)
#define MVPP2_PRS_RI_L4_OTHER (BIT(22) | BIT(23))
#define MVPP2_PRS_RI_UDF7_MASK 0x60000000
#define MVPP2_PRS_RI_UDF7_IP6_LITE BIT(29)
#define MVPP2_PRS_RI_DROP_MASK 0x80000000
#define MVPP2_PRS_IP_MASK (MVPP2_PRS_RI_L3_PROTO_MASK | \
MVPP2_PRS_RI_IP_FRAG_MASK | \
MVPP2_PRS_RI_L4_PROTO_MASK)
/* Sram additional info bits assignment */
#define MVPP2_PRS_IPV4_DIP_AI_BIT BIT(0)
#define MVPP2_PRS_IPV6_NO_EXT_AI_BIT BIT(0)
#define MVPP2_PRS_IPV6_EXT_AI_BIT BIT(1)
#define MVPP2_PRS_IPV6_EXT_AH_AI_BIT BIT(2)
#define MVPP2_PRS_IPV6_EXT_AH_LEN_AI_BIT BIT(3)
#define MVPP2_PRS_IPV6_EXT_AH_L4_AI_BIT BIT(4)
#define MVPP2_PRS_SINGLE_VLAN_AI 0
#define MVPP2_PRS_DBL_VLAN_AI_BIT BIT(7)
#define MVPP2_PRS_EDSA_VID_AI_BIT BIT(0)
/* DSA/EDSA type */
#define MVPP2_PRS_TAGGED true
#define MVPP2_PRS_UNTAGGED false
#define MVPP2_PRS_EDSA true
#define MVPP2_PRS_DSA false
/* MAC entries, shadow udf */
enum mvpp2_prs_udf {
MVPP2_PRS_UDF_MAC_DEF,
MVPP2_PRS_UDF_MAC_RANGE,
MVPP2_PRS_UDF_L2_DEF,
MVPP2_PRS_UDF_L2_DEF_COPY,
MVPP2_PRS_UDF_L2_USER,
};
/* Lookup ID */
enum mvpp2_prs_lookup {
MVPP2_PRS_LU_MH,
MVPP2_PRS_LU_MAC,
MVPP2_PRS_LU_DSA,
MVPP2_PRS_LU_VLAN,
MVPP2_PRS_LU_VID,
MVPP2_PRS_LU_L2,
MVPP2_PRS_LU_PPPOE,
MVPP2_PRS_LU_IP4,
MVPP2_PRS_LU_IP6,
MVPP2_PRS_LU_FLOWS,
MVPP2_PRS_LU_LAST,
};
struct mvpp2_prs_entry {
u32 index;
u32 tcam[MVPP2_PRS_TCAM_WORDS];
u32 sram[MVPP2_PRS_SRAM_WORDS];
};
struct mvpp2_prs_result_info {
u32 ri;
u32 ri_mask;
};
struct mvpp2_prs_shadow {
bool valid;
bool finish;
/* Lookup ID */
int lu;
/* User defined offset */
int udf;
/* Result info */
u32 ri;
u32 ri_mask;
};
int mvpp2_prs_default_init(struct platform_device *pdev, struct mvpp2 *priv);
int mvpp2_prs_init_from_hw(struct mvpp2 *priv, struct mvpp2_prs_entry *pe,
int tid);
unsigned int mvpp2_prs_tcam_port_map_get(struct mvpp2_prs_entry *pe);
void mvpp2_prs_tcam_data_byte_get(struct mvpp2_prs_entry *pe,
unsigned int offs, unsigned char *byte,
unsigned char *enable);
int mvpp2_prs_mac_da_accept(struct mvpp2_port *port, const u8 *da, bool add);
int mvpp2_prs_tag_mode_set(struct mvpp2 *priv, int port, int type);
int mvpp2_prs_add_flow(struct mvpp2 *priv, int flow, u32 ri, u32 ri_mask);
int mvpp2_prs_def_flow(struct mvpp2_port *port);
void mvpp2_prs_vid_enable_filtering(struct mvpp2_port *port);
void mvpp2_prs_vid_disable_filtering(struct mvpp2_port *port);
int mvpp2_prs_vid_entry_add(struct mvpp2_port *port, u16 vid);
void mvpp2_prs_vid_entry_remove(struct mvpp2_port *port, u16 vid);
void mvpp2_prs_vid_remove_all(struct mvpp2_port *port);
void mvpp2_prs_mac_promisc_set(struct mvpp2 *priv, int port,
enum mvpp2_prs_l2_cast l2_cast, bool add);
void mvpp2_prs_mac_del_all(struct mvpp2_port *port);
int mvpp2_prs_update_mac_da(struct net_device *dev, const u8 *da);
int mvpp2_prs_hits(struct mvpp2 *priv, int index);
#endif
@@ -0,0 +1,457 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Marvell PP2.2 TAI support
*
* Note:
* Do NOT use the event capture support.
* Do Not even set the MPP muxes to allow PTP_EVENT_REQ to be used.
* It will disrupt the operation of this driver, and there is nothing
* that this driver can do to prevent that. Even using PTP_EVENT_REQ
* as an output will be seen as a trigger input, which can't be masked.
* When ever a trigger input is seen, the action in the TCFCR0_TCF
* field will be performed - whether it is a set, increment, decrement
* read, or frequency update.
*
* Other notes (useful, not specified in the documentation):
* - PTP_PULSE_OUT (PTP_EVENT_REQ MPP)
* It looks like the hardware can't generate a pulse at nsec=0. (The
* output doesn't trigger if the nsec field is zero.)
* Note: when configured as an output via the register at 0xfX441120,
* the input is still very much alive, and will trigger the current TCF
* function.
* - PTP_CLK_OUT (PTP_TRIG_GEN MPP)
* This generates a "PPS" signal determined by the CCC registers. It
* seems this is not aligned to the TOD counter in any way (it may be
* initially, but if you specify a non-round second interval, it won't,
* and you can't easily get it back.)
* - PTP_PCLK_OUT
* This generates a 50% duty cycle clock based on the TOD counter, and
* seems it can be set to any period of 1ns resolution. It is probably
* limited by the TOD step size. Its period is defined by the PCLK_CCC
* registers. Again, its alignment to the second is questionable.
*
* Consequently, we support none of these.
*/
#include <linux/io.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/slab.h>
#include "mvpp2.h"
#define CR0_SW_NRESET BIT(0)
#define TCFCR0_PHASE_UPDATE_ENABLE BIT(8)
#define TCFCR0_TCF_MASK (7 << 2)
#define TCFCR0_TCF_UPDATE (0 << 2)
#define TCFCR0_TCF_FREQUPDATE (1 << 2)
#define TCFCR0_TCF_INCREMENT (2 << 2)
#define TCFCR0_TCF_DECREMENT (3 << 2)
#define TCFCR0_TCF_CAPTURE (4 << 2)
#define TCFCR0_TCF_NOP (7 << 2)
#define TCFCR0_TCF_TRIGGER BIT(0)
#define TCSR_CAPTURE_1_VALID BIT(1)
#define TCSR_CAPTURE_0_VALID BIT(0)
struct mvpp2_tai {
struct ptp_clock_info caps;
struct ptp_clock *ptp_clock;
void __iomem *base;
spinlock_t lock;
u64 period; // nanosecond period in 32.32 fixed point
/* This timestamp is updated every two seconds */
struct timespec64 stamp;
};
static void mvpp2_tai_modify(void __iomem *reg, u32 mask, u32 set)
{
u32 val;
val = readl_relaxed(reg) & ~mask;
val |= set & mask;
writel(val, reg);
}
static void mvpp2_tai_write(u32 val, void __iomem *reg)
{
writel_relaxed(val & 0xffff, reg);
}
static u32 mvpp2_tai_read(void __iomem *reg)
{
return readl_relaxed(reg) & 0xffff;
}
static struct mvpp2_tai *ptp_to_tai(struct ptp_clock_info *ptp)
{
return container_of(ptp, struct mvpp2_tai, caps);
}
static void mvpp22_tai_read_ts(struct timespec64 *ts, void __iomem *base)
{
ts->tv_sec = (u64)mvpp2_tai_read(base + 0) << 32 |
mvpp2_tai_read(base + 4) << 16 |
mvpp2_tai_read(base + 8);
ts->tv_nsec = mvpp2_tai_read(base + 12) << 16 |
mvpp2_tai_read(base + 16);
/* Read and discard fractional part */
readl_relaxed(base + 20);
readl_relaxed(base + 24);
}
static void mvpp2_tai_write_tlv(const struct timespec64 *ts, u32 frac,
void __iomem *base)
{
mvpp2_tai_write(ts->tv_sec >> 32, base + MVPP22_TAI_TLV_SEC_HIGH);
mvpp2_tai_write(ts->tv_sec >> 16, base + MVPP22_TAI_TLV_SEC_MED);
mvpp2_tai_write(ts->tv_sec, base + MVPP22_TAI_TLV_SEC_LOW);
mvpp2_tai_write(ts->tv_nsec >> 16, base + MVPP22_TAI_TLV_NANO_HIGH);
mvpp2_tai_write(ts->tv_nsec, base + MVPP22_TAI_TLV_NANO_LOW);
mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TLV_FRAC_HIGH);
mvpp2_tai_write(frac, base + MVPP22_TAI_TLV_FRAC_LOW);
}
static void mvpp2_tai_op(u32 op, void __iomem *base)
{
/* Trigger the operation. Note that an external unmaskable
* event on PTP_EVENT_REQ will also trigger this action.
*/
mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
op | TCFCR0_TCF_TRIGGER);
mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
TCFCR0_TCF_NOP);
}
/* The adjustment has a range of +0.5ns to -0.5ns in 2^32 steps, so has units
* of 2^-32 ns.
*
* units(s) = 1 / (2^32 * 10^9)
* fractional = abs_scaled_ppm / (2^16 * 10^6)
*
* What we want to achieve:
* freq_adjusted = freq_nominal * (1 + fractional)
* freq_delta = freq_adjusted - freq_nominal => positive = faster
* freq_delta = freq_nominal * (1 + fractional) - freq_nominal
* So: freq_delta = freq_nominal * fractional
*
* However, we are dealing with periods, so:
* period_adjusted = period_nominal / (1 + fractional)
* period_delta = period_nominal - period_adjusted => positive = faster
* period_delta = period_nominal * fractional / (1 + fractional)
*
* Hence:
* period_delta = period_nominal * abs_scaled_ppm /
* (2^16 * 10^6 + abs_scaled_ppm)
*
* To avoid overflow, we reduce both sides of the divide operation by a factor
* of 16.
*/
static u64 mvpp22_calc_frac_ppm(struct mvpp2_tai *tai, long abs_scaled_ppm)
{
u64 val = tai->period * abs_scaled_ppm >> 4;
return div_u64(val, (1000000 << 12) + (abs_scaled_ppm >> 4));
}
static s32 mvpp22_calc_max_adj(struct mvpp2_tai *tai)
{
return 1000000;
}
static int mvpp22_tai_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct mvpp2_tai *tai = ptp_to_tai(ptp);
unsigned long flags;
void __iomem *base;
bool neg_adj;
s32 frac;
u64 val;
neg_adj = scaled_ppm < 0;
if (neg_adj)
scaled_ppm = -scaled_ppm;
val = mvpp22_calc_frac_ppm(tai, scaled_ppm);
/* Convert to a signed 32-bit adjustment */
if (neg_adj) {
/* -S32_MIN warns, -val < S32_MIN fails, so go for the easy
* solution.
*/
if (val > 0x80000000)
return -ERANGE;
frac = -val;
} else {
if (val > S32_MAX)
return -ERANGE;
frac = val;
}
base = tai->base;
spin_lock_irqsave(&tai->lock, flags);
mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TLV_FRAC_HIGH);
mvpp2_tai_write(frac, base + MVPP22_TAI_TLV_FRAC_LOW);
mvpp2_tai_op(TCFCR0_TCF_FREQUPDATE, base);
spin_unlock_irqrestore(&tai->lock, flags);
return 0;
}
static int mvpp22_tai_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct mvpp2_tai *tai = ptp_to_tai(ptp);
struct timespec64 ts;
unsigned long flags;
void __iomem *base;
u32 tcf;
/* We can't deal with S64_MIN */
if (delta == S64_MIN)
return -ERANGE;
if (delta < 0) {
delta = -delta;
tcf = TCFCR0_TCF_DECREMENT;
} else {
tcf = TCFCR0_TCF_INCREMENT;
}
ts = ns_to_timespec64(delta);
base = tai->base;
spin_lock_irqsave(&tai->lock, flags);
mvpp2_tai_write_tlv(&ts, 0, base);
mvpp2_tai_op(tcf, base);
spin_unlock_irqrestore(&tai->lock, flags);
return 0;
}
static int mvpp22_tai_gettimex64(struct ptp_clock_info *ptp,
struct timespec64 *ts,
struct ptp_system_timestamp *sts)
{
struct mvpp2_tai *tai = ptp_to_tai(ptp);
unsigned long flags;
void __iomem *base;
u32 tcsr;
int ret;
base = tai->base;
spin_lock_irqsave(&tai->lock, flags);
/* XXX: the only way to read the PTP time is for the CPU to trigger
* an event. However, there is no way to distinguish between the CPU
* triggered event, and an external event on PTP_EVENT_REQ. So this
* is incompatible with external use of PTP_EVENT_REQ.
*/
ptp_read_system_prets(sts);
mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
TCFCR0_TCF_CAPTURE | TCFCR0_TCF_TRIGGER);
ptp_read_system_postts(sts);
mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
TCFCR0_TCF_NOP);
tcsr = readl(base + MVPP22_TAI_TCSR);
if (tcsr & TCSR_CAPTURE_1_VALID) {
mvpp22_tai_read_ts(ts, base + MVPP22_TAI_TCV1_SEC_HIGH);
ret = 0;
} else if (tcsr & TCSR_CAPTURE_0_VALID) {
mvpp22_tai_read_ts(ts, base + MVPP22_TAI_TCV0_SEC_HIGH);
ret = 0;
} else {
/* We don't seem to have a reading... */
ret = -EBUSY;
}
spin_unlock_irqrestore(&tai->lock, flags);
return ret;
}
static int mvpp22_tai_settime64(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct mvpp2_tai *tai = ptp_to_tai(ptp);
unsigned long flags;
void __iomem *base;
base = tai->base;
spin_lock_irqsave(&tai->lock, flags);
mvpp2_tai_write_tlv(ts, 0, base);
/* Trigger an update to load the value from the TLV registers
* into the TOD counter. Note that an external unmaskable event on
* PTP_EVENT_REQ will also trigger this action.
*/
mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0,
TCFCR0_PHASE_UPDATE_ENABLE |
TCFCR0_TCF_MASK | TCFCR0_TCF_TRIGGER,
TCFCR0_TCF_UPDATE | TCFCR0_TCF_TRIGGER);
mvpp2_tai_modify(base + MVPP22_TAI_TCFCR0, TCFCR0_TCF_MASK,
TCFCR0_TCF_NOP);
spin_unlock_irqrestore(&tai->lock, flags);
return 0;
}
static long mvpp22_tai_aux_work(struct ptp_clock_info *ptp)
{
struct mvpp2_tai *tai = ptp_to_tai(ptp);
mvpp22_tai_gettimex64(ptp, &tai->stamp, NULL);
return msecs_to_jiffies(2000);
}
static void mvpp22_tai_set_step(struct mvpp2_tai *tai)
{
void __iomem *base = tai->base;
u32 nano, frac;
nano = upper_32_bits(tai->period);
frac = lower_32_bits(tai->period);
/* As the fractional nanosecond is a signed offset, if the MSB (sign)
* bit is set, we have to increment the whole nanoseconds.
*/
if (frac >= 0x80000000)
nano += 1;
mvpp2_tai_write(nano, base + MVPP22_TAI_TOD_STEP_NANO_CR);
mvpp2_tai_write(frac >> 16, base + MVPP22_TAI_TOD_STEP_FRAC_HIGH);
mvpp2_tai_write(frac, base + MVPP22_TAI_TOD_STEP_FRAC_LOW);
}
static void mvpp22_tai_init(struct mvpp2_tai *tai)
{
void __iomem *base = tai->base;
mvpp22_tai_set_step(tai);
/* Release the TAI reset */
mvpp2_tai_modify(base + MVPP22_TAI_CR0, CR0_SW_NRESET, CR0_SW_NRESET);
}
int mvpp22_tai_ptp_clock_index(struct mvpp2_tai *tai)
{
return ptp_clock_index(tai->ptp_clock);
}
void mvpp22_tai_tstamp(struct mvpp2_tai *tai, u32 tstamp,
struct skb_shared_hwtstamps *hwtstamp)
{
struct timespec64 ts;
int delta;
/* The tstamp consists of 2 bits of seconds and 30 bits of nanoseconds.
* We use our stored timestamp (tai->stamp) to form a full timestamp,
* and we must read the seconds exactly once.
*/
ts.tv_sec = READ_ONCE(tai->stamp.tv_sec);
ts.tv_nsec = tstamp & 0x3fffffff;
/* Calculate the delta in seconds between our stored timestamp and
* the value read from the queue. Allow timestamps one second in the
* past, otherwise consider them to be in the future.
*/
delta = ((tstamp >> 30) - (ts.tv_sec & 3)) & 3;
if (delta == 3)
delta -= 4;
ts.tv_sec += delta;
memset(hwtstamp, 0, sizeof(*hwtstamp));
hwtstamp->hwtstamp = timespec64_to_ktime(ts);
}
void mvpp22_tai_start(struct mvpp2_tai *tai)
{
long delay;
delay = mvpp22_tai_aux_work(&tai->caps);
ptp_schedule_worker(tai->ptp_clock, delay);
}
void mvpp22_tai_stop(struct mvpp2_tai *tai)
{
ptp_cancel_worker_sync(tai->ptp_clock);
}
static void mvpp22_tai_remove(void *priv)
{
struct mvpp2_tai *tai = priv;
if (!IS_ERR(tai->ptp_clock))
ptp_clock_unregister(tai->ptp_clock);
}
int mvpp22_tai_probe(struct device *dev, struct mvpp2 *priv)
{
struct mvpp2_tai *tai;
int ret;
tai = devm_kzalloc(dev, sizeof(*tai), GFP_KERNEL);
if (!tai)
return -ENOMEM;
spin_lock_init(&tai->lock);
tai->base = priv->iface_base;
/* The step size consists of three registers - a 16-bit nanosecond step
* size, and a 32-bit fractional nanosecond step size split over two
* registers. The fractional nanosecond step size has units of 2^-32ns.
*
* To calculate this, we calculate:
* (10^9 + freq / 2) / (freq * 2^-32)
* which gives us the nanosecond step to the nearest integer in 16.32
* fixed point format, and the fractional part of the step size with
* the MSB inverted. With rounding of the fractional nanosecond, and
* simplification, this becomes:
* (10^9 << 32 + freq << 31 + (freq + 1) >> 1) / freq
*
* So:
* div = (10^9 << 32 + freq << 31 + (freq + 1) >> 1) / freq
* nano = upper_32_bits(div);
* frac = lower_32_bits(div) ^ 0x80000000;
* Will give the values for the registers.
*
* This is all seems perfect, but alas it is not when considering the
* whole story. The system is clocked from 25MHz, which is multiplied
* by a PLL to 1GHz, and then divided by three, giving 333333333Hz
* (recurring). This gives exactly 3ns, but using 333333333Hz with
* the above gives an error of 13*2^-32ns.
*
* Consequently, we use the period rather than calculating from the
* frequency.
*/
tai->period = 3ULL << 32;
mvpp22_tai_init(tai);
tai->caps.owner = THIS_MODULE;
strscpy(tai->caps.name, "Marvell PP2.2", sizeof(tai->caps.name));
tai->caps.max_adj = mvpp22_calc_max_adj(tai);
tai->caps.adjfine = mvpp22_tai_adjfine;
tai->caps.adjtime = mvpp22_tai_adjtime;
tai->caps.gettimex64 = mvpp22_tai_gettimex64;
tai->caps.settime64 = mvpp22_tai_settime64;
tai->caps.do_aux_work = mvpp22_tai_aux_work;
ret = devm_add_action(dev, mvpp22_tai_remove, tai);
if (ret)
return ret;
tai->ptp_clock = ptp_clock_register(&tai->caps, dev);
if (IS_ERR(tai->ptp_clock))
return PTR_ERR(tai->ptp_clock);
priv->tai = tai;
return 0;
}