2035 lines
56 KiB
C
2035 lines
56 KiB
C
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
||
|
|
/*
|
||
|
|
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/* PPE HW initialization configs such as BM(buffer management),
|
||
|
|
* QM(queue management) and scheduler configs.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <linux/bitfield.h>
|
||
|
|
#include <linux/bitmap.h>
|
||
|
|
#include <linux/bits.h>
|
||
|
|
#include <linux/device.h>
|
||
|
|
#include <linux/regmap.h>
|
||
|
|
|
||
|
|
#include "ppe.h"
|
||
|
|
#include "ppe_config.h"
|
||
|
|
#include "ppe_regs.h"
|
||
|
|
|
||
|
|
#define PPE_QUEUE_SCH_PRI_NUM 8
|
||
|
|
|
||
|
|
/**
|
||
|
|
* struct ppe_bm_port_config - PPE BM port configuration.
|
||
|
|
* @port_id_start: The fist BM port ID to configure.
|
||
|
|
* @port_id_end: The last BM port ID to configure.
|
||
|
|
* @pre_alloc: BM port dedicated buffer number.
|
||
|
|
* @in_fly_buf: Buffer number for receiving the packet after pause frame sent.
|
||
|
|
* @ceil: Ceil to generate the back pressure.
|
||
|
|
* @weight: Weight value.
|
||
|
|
* @resume_offset: Resume offset from the threshold value.
|
||
|
|
* @resume_ceil: Ceil to resume from the back pressure state.
|
||
|
|
* @dynamic: Dynamic threshold used or not.
|
||
|
|
*
|
||
|
|
* The is for configuring the threshold that impacts the port
|
||
|
|
* flow control.
|
||
|
|
*/
|
||
|
|
struct ppe_bm_port_config {
|
||
|
|
unsigned int port_id_start;
|
||
|
|
unsigned int port_id_end;
|
||
|
|
unsigned int pre_alloc;
|
||
|
|
unsigned int in_fly_buf;
|
||
|
|
unsigned int ceil;
|
||
|
|
unsigned int weight;
|
||
|
|
unsigned int resume_offset;
|
||
|
|
unsigned int resume_ceil;
|
||
|
|
bool dynamic;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* struct ppe_qm_queue_config - PPE queue config.
|
||
|
|
* @queue_start: PPE start of queue ID.
|
||
|
|
* @queue_end: PPE end of queue ID.
|
||
|
|
* @prealloc_buf: Queue dedicated buffer number.
|
||
|
|
* @ceil: Ceil to start drop packet from queue.
|
||
|
|
* @weight: Weight value.
|
||
|
|
* @resume_offset: Resume offset from the threshold.
|
||
|
|
* @dynamic: Threshold value is decided dynamically or statically.
|
||
|
|
*
|
||
|
|
* Queue configuration decides the threshold to drop packet from PPE
|
||
|
|
* hardware queue.
|
||
|
|
*/
|
||
|
|
struct ppe_qm_queue_config {
|
||
|
|
unsigned int queue_start;
|
||
|
|
unsigned int queue_end;
|
||
|
|
unsigned int prealloc_buf;
|
||
|
|
unsigned int ceil;
|
||
|
|
unsigned int weight;
|
||
|
|
unsigned int resume_offset;
|
||
|
|
bool dynamic;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* enum ppe_scheduler_direction - PPE scheduler direction for packet.
|
||
|
|
* @PPE_SCH_INGRESS: Scheduler for the packet on ingress,
|
||
|
|
* @PPE_SCH_EGRESS: Scheduler for the packet on egress,
|
||
|
|
*/
|
||
|
|
enum ppe_scheduler_direction {
|
||
|
|
PPE_SCH_INGRESS = 0,
|
||
|
|
PPE_SCH_EGRESS = 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* struct ppe_scheduler_bm_config - PPE arbitration for buffer config.
|
||
|
|
* @valid: Arbitration entry valid or not.
|
||
|
|
* @dir: Arbitration entry for egress or ingress.
|
||
|
|
* @port: Port ID to use arbitration entry.
|
||
|
|
* @backup_port_valid: Backup port valid or not.
|
||
|
|
* @backup_port: Backup port ID to use.
|
||
|
|
*
|
||
|
|
* Configure the scheduler settings for accessing and releasing the PPE buffers.
|
||
|
|
*/
|
||
|
|
struct ppe_scheduler_bm_config {
|
||
|
|
bool valid;
|
||
|
|
enum ppe_scheduler_direction dir;
|
||
|
|
unsigned int port;
|
||
|
|
bool backup_port_valid;
|
||
|
|
unsigned int backup_port;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* struct ppe_scheduler_qm_config - PPE arbitration for scheduler config.
|
||
|
|
* @ensch_port_bmp: Port bit map for enqueue scheduler.
|
||
|
|
* @ensch_port: Port ID to enqueue scheduler.
|
||
|
|
* @desch_port: Port ID to dequeue scheduler.
|
||
|
|
* @desch_backup_port_valid: Dequeue for the backup port valid or not.
|
||
|
|
* @desch_backup_port: Backup port ID to dequeue scheduler.
|
||
|
|
*
|
||
|
|
* Configure the scheduler settings for enqueuing and dequeuing packets on
|
||
|
|
* the PPE port.
|
||
|
|
*/
|
||
|
|
struct ppe_scheduler_qm_config {
|
||
|
|
unsigned int ensch_port_bmp;
|
||
|
|
unsigned int ensch_port;
|
||
|
|
unsigned int desch_port;
|
||
|
|
bool desch_backup_port_valid;
|
||
|
|
unsigned int desch_backup_port;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* struct ppe_scheduler_port_config - PPE port scheduler config.
|
||
|
|
* @port: Port ID to be scheduled.
|
||
|
|
* @flow_level: Scheduler flow level or not.
|
||
|
|
* @node_id: Node ID, for level 0, queue ID is used.
|
||
|
|
* @loop_num: Loop number of scheduler config.
|
||
|
|
* @pri_max: Max priority configured.
|
||
|
|
* @flow_id: Strict priority ID.
|
||
|
|
* @drr_node_id: Node ID for scheduler.
|
||
|
|
*
|
||
|
|
* PPE port scheduler configuration which decides the priority in the
|
||
|
|
* packet scheduler for the egress port.
|
||
|
|
*/
|
||
|
|
struct ppe_scheduler_port_config {
|
||
|
|
unsigned int port;
|
||
|
|
bool flow_level;
|
||
|
|
unsigned int node_id;
|
||
|
|
unsigned int loop_num;
|
||
|
|
unsigned int pri_max;
|
||
|
|
unsigned int flow_id;
|
||
|
|
unsigned int drr_node_id;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* struct ppe_port_schedule_resource - PPE port scheduler resource.
|
||
|
|
* @ucastq_start: Unicast queue start ID.
|
||
|
|
* @ucastq_end: Unicast queue end ID.
|
||
|
|
* @mcastq_start: Multicast queue start ID.
|
||
|
|
* @mcastq_end: Multicast queue end ID.
|
||
|
|
* @flow_id_start: Flow start ID.
|
||
|
|
* @flow_id_end: Flow end ID.
|
||
|
|
* @l0node_start: Scheduler node start ID for queue level.
|
||
|
|
* @l0node_end: Scheduler node end ID for queue level.
|
||
|
|
* @l1node_start: Scheduler node start ID for flow level.
|
||
|
|
* @l1node_end: Scheduler node end ID for flow level.
|
||
|
|
*
|
||
|
|
* PPE scheduler resource allocated among the PPE ports.
|
||
|
|
*/
|
||
|
|
struct ppe_port_schedule_resource {
|
||
|
|
unsigned int ucastq_start;
|
||
|
|
unsigned int ucastq_end;
|
||
|
|
unsigned int mcastq_start;
|
||
|
|
unsigned int mcastq_end;
|
||
|
|
unsigned int flow_id_start;
|
||
|
|
unsigned int flow_id_end;
|
||
|
|
unsigned int l0node_start;
|
||
|
|
unsigned int l0node_end;
|
||
|
|
unsigned int l1node_start;
|
||
|
|
unsigned int l1node_end;
|
||
|
|
};
|
||
|
|
|
||
|
|
/* There are total 2048 buffers available in PPE, out of which some
|
||
|
|
* buffers are reserved for some specific purposes per PPE port. The
|
||
|
|
* rest of the pool of 1550 buffers are assigned to the general 'group0'
|
||
|
|
* which is shared among all ports of the PPE.
|
||
|
|
*/
|
||
|
|
static const int ipq9574_ppe_bm_group_config = 1550;
|
||
|
|
|
||
|
|
/* The buffer configurations per PPE port. There are 15 BM ports and
|
||
|
|
* 4 BM groups supported by PPE. BM port (0-7) is for EDMA port 0,
|
||
|
|
* BM port (8-13) is for PPE physical port 1-6 and BM port 14 is for
|
||
|
|
* EIP port.
|
||
|
|
*/
|
||
|
|
static const struct ppe_bm_port_config ipq9574_ppe_bm_port_config[] = {
|
||
|
|
{
|
||
|
|
/* Buffer configuration for the BM port ID 0 of EDMA. */
|
||
|
|
.port_id_start = 0,
|
||
|
|
.port_id_end = 0,
|
||
|
|
.pre_alloc = 0,
|
||
|
|
.in_fly_buf = 100,
|
||
|
|
.ceil = 1146,
|
||
|
|
.weight = 7,
|
||
|
|
.resume_offset = 8,
|
||
|
|
.resume_ceil = 0,
|
||
|
|
.dynamic = true,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
/* Buffer configuration for the BM port ID 1-7 of EDMA. */
|
||
|
|
.port_id_start = 1,
|
||
|
|
.port_id_end = 7,
|
||
|
|
.pre_alloc = 0,
|
||
|
|
.in_fly_buf = 100,
|
||
|
|
.ceil = 250,
|
||
|
|
.weight = 4,
|
||
|
|
.resume_offset = 36,
|
||
|
|
.resume_ceil = 0,
|
||
|
|
.dynamic = true,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
/* Buffer configuration for the BM port ID 8-13 of PPE ports. */
|
||
|
|
.port_id_start = 8,
|
||
|
|
.port_id_end = 13,
|
||
|
|
.pre_alloc = 0,
|
||
|
|
.in_fly_buf = 128,
|
||
|
|
.ceil = 250,
|
||
|
|
.weight = 4,
|
||
|
|
.resume_offset = 36,
|
||
|
|
.resume_ceil = 0,
|
||
|
|
.dynamic = true,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
/* Buffer configuration for the BM port ID 14 of EIP. */
|
||
|
|
.port_id_start = 14,
|
||
|
|
.port_id_end = 14,
|
||
|
|
.pre_alloc = 0,
|
||
|
|
.in_fly_buf = 40,
|
||
|
|
.ceil = 250,
|
||
|
|
.weight = 4,
|
||
|
|
.resume_offset = 36,
|
||
|
|
.resume_ceil = 0,
|
||
|
|
.dynamic = true,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
/* QM fetches the packet from PPE buffer management for transmitting the
|
||
|
|
* packet out. The QM group configuration limits the total number of buffers
|
||
|
|
* enqueued by all PPE hardware queues.
|
||
|
|
* There are total 2048 buffers available, out of which some buffers are
|
||
|
|
* dedicated to hardware exception handlers. The remaining buffers are
|
||
|
|
* assigned to the general 'group0', which is the group assigned to all
|
||
|
|
* queues by default.
|
||
|
|
*/
|
||
|
|
static const int ipq9574_ppe_qm_group_config = 2000;
|
||
|
|
|
||
|
|
/* Default QM settings for unicast and multicast queues for IPQ9754. */
|
||
|
|
static const struct ppe_qm_queue_config ipq9574_ppe_qm_queue_config[] = {
|
||
|
|
{
|
||
|
|
/* QM settings for unicast queues 0 to 255. */
|
||
|
|
.queue_start = 0,
|
||
|
|
.queue_end = 255,
|
||
|
|
.prealloc_buf = 0,
|
||
|
|
.ceil = 1200,
|
||
|
|
.weight = 7,
|
||
|
|
.resume_offset = 36,
|
||
|
|
.dynamic = true,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
/* QM settings for multicast queues 256 to 299. */
|
||
|
|
.queue_start = 256,
|
||
|
|
.queue_end = 299,
|
||
|
|
.prealloc_buf = 0,
|
||
|
|
.ceil = 250,
|
||
|
|
.weight = 0,
|
||
|
|
.resume_offset = 36,
|
||
|
|
.dynamic = false,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
/* PPE scheduler configuration for BM includes multiple entries. Each entry
|
||
|
|
* indicates the primary port to be assigned the buffers for the ingress or
|
||
|
|
* to release the buffers for the egress. Backup port ID will be used when
|
||
|
|
* the primary port ID is down.
|
||
|
|
*/
|
||
|
|
static const struct ppe_scheduler_bm_config ipq9574_ppe_sch_bm_config[] = {
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 7, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 7, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 2, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 2, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 3, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 3, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 7, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 7, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 4, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 4, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 2, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 2, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 7, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 7, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 3, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 3, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 1, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 0, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 5, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 6, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 4, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 4, false, 0},
|
||
|
|
{true, PPE_SCH_INGRESS, 7, false, 0},
|
||
|
|
{true, PPE_SCH_EGRESS, 7, false, 0},
|
||
|
|
};
|
||
|
|
|
||
|
|
/* PPE scheduler configuration for QM includes multiple entries. Each entry
|
||
|
|
* contains ports to be dispatched for enqueueing and dequeueing. The backup
|
||
|
|
* port for dequeueing is supported to be used when the primary port for
|
||
|
|
* dequeueing is down.
|
||
|
|
*/
|
||
|
|
static const struct ppe_scheduler_qm_config ipq9574_ppe_sch_qm_config[] = {
|
||
|
|
{0x98, 6, 0, true, 1},
|
||
|
|
{0x94, 5, 6, true, 3},
|
||
|
|
{0x86, 0, 5, true, 4},
|
||
|
|
{0x8C, 1, 6, true, 0},
|
||
|
|
{0x1C, 7, 5, true, 1},
|
||
|
|
{0x98, 2, 6, true, 0},
|
||
|
|
{0x1C, 5, 7, true, 1},
|
||
|
|
{0x34, 3, 6, true, 0},
|
||
|
|
{0x8C, 4, 5, true, 1},
|
||
|
|
{0x98, 2, 6, true, 0},
|
||
|
|
{0x8C, 5, 4, true, 1},
|
||
|
|
{0xA8, 0, 6, true, 2},
|
||
|
|
{0x98, 5, 1, true, 0},
|
||
|
|
{0x98, 6, 5, true, 2},
|
||
|
|
{0x89, 1, 6, true, 4},
|
||
|
|
{0xA4, 3, 0, true, 1},
|
||
|
|
{0x8C, 5, 6, true, 4},
|
||
|
|
{0xA8, 0, 2, true, 1},
|
||
|
|
{0x98, 6, 5, true, 0},
|
||
|
|
{0xC4, 4, 3, true, 1},
|
||
|
|
{0x94, 6, 5, true, 0},
|
||
|
|
{0x1C, 7, 6, true, 1},
|
||
|
|
{0x98, 2, 5, true, 0},
|
||
|
|
{0x1C, 6, 7, true, 1},
|
||
|
|
{0x1C, 5, 6, true, 0},
|
||
|
|
{0x94, 3, 5, true, 1},
|
||
|
|
{0x8C, 4, 6, true, 0},
|
||
|
|
{0x94, 1, 5, true, 3},
|
||
|
|
{0x94, 6, 1, true, 0},
|
||
|
|
{0xD0, 3, 5, true, 2},
|
||
|
|
{0x98, 6, 0, true, 1},
|
||
|
|
{0x94, 5, 6, true, 3},
|
||
|
|
{0x94, 1, 5, true, 0},
|
||
|
|
{0x98, 2, 6, true, 1},
|
||
|
|
{0x8C, 4, 5, true, 0},
|
||
|
|
{0x1C, 7, 6, true, 1},
|
||
|
|
{0x8C, 0, 5, true, 4},
|
||
|
|
{0x89, 1, 6, true, 2},
|
||
|
|
{0x98, 5, 0, true, 1},
|
||
|
|
{0x94, 6, 5, true, 3},
|
||
|
|
{0x92, 0, 6, true, 2},
|
||
|
|
{0x98, 1, 5, true, 0},
|
||
|
|
{0x98, 6, 2, true, 1},
|
||
|
|
{0xD0, 0, 5, true, 3},
|
||
|
|
{0x94, 6, 0, true, 1},
|
||
|
|
{0x8C, 5, 6, true, 4},
|
||
|
|
{0x8C, 1, 5, true, 0},
|
||
|
|
{0x1C, 6, 7, true, 1},
|
||
|
|
{0x1C, 5, 6, true, 0},
|
||
|
|
{0xB0, 2, 3, true, 1},
|
||
|
|
{0xC4, 4, 5, true, 0},
|
||
|
|
{0x8C, 6, 4, true, 1},
|
||
|
|
{0xA4, 3, 6, true, 0},
|
||
|
|
{0x1C, 5, 7, true, 1},
|
||
|
|
{0x4C, 0, 5, true, 4},
|
||
|
|
{0x8C, 6, 0, true, 1},
|
||
|
|
{0x34, 7, 6, true, 3},
|
||
|
|
{0x94, 5, 0, true, 1},
|
||
|
|
{0x98, 6, 5, true, 2},
|
||
|
|
};
|
||
|
|
|
||
|
|
static const struct ppe_scheduler_port_config ppe_port_sch_config[] = {
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = true,
|
||
|
|
.node_id = 0,
|
||
|
|
.loop_num = 1,
|
||
|
|
.pri_max = 1,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 0,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 8,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 16,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 24,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 32,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 40,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 48,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 56,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 256,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 0,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 264,
|
||
|
|
.loop_num = 8,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 0,
|
||
|
|
.drr_node_id = 0,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 1,
|
||
|
|
.flow_level = true,
|
||
|
|
.node_id = 36,
|
||
|
|
.loop_num = 2,
|
||
|
|
.pri_max = 0,
|
||
|
|
.flow_id = 1,
|
||
|
|
.drr_node_id = 8,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 1,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 144,
|
||
|
|
.loop_num = 16,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 36,
|
||
|
|
.drr_node_id = 48,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 1,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 272,
|
||
|
|
.loop_num = 4,
|
||
|
|
.pri_max = 4,
|
||
|
|
.flow_id = 36,
|
||
|
|
.drr_node_id = 48,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 2,
|
||
|
|
.flow_level = true,
|
||
|
|
.node_id = 40,
|
||
|
|
.loop_num = 2,
|
||
|
|
.pri_max = 0,
|
||
|
|
.flow_id = 2,
|
||
|
|
.drr_node_id = 12,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 2,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 160,
|
||
|
|
.loop_num = 16,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 40,
|
||
|
|
.drr_node_id = 64,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 2,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 276,
|
||
|
|
.loop_num = 4,
|
||
|
|
.pri_max = 4,
|
||
|
|
.flow_id = 40,
|
||
|
|
.drr_node_id = 64,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 3,
|
||
|
|
.flow_level = true,
|
||
|
|
.node_id = 44,
|
||
|
|
.loop_num = 2,
|
||
|
|
.pri_max = 0,
|
||
|
|
.flow_id = 3,
|
||
|
|
.drr_node_id = 16,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 3,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 176,
|
||
|
|
.loop_num = 16,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 44,
|
||
|
|
.drr_node_id = 80,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 3,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 280,
|
||
|
|
.loop_num = 4,
|
||
|
|
.pri_max = 4,
|
||
|
|
.flow_id = 44,
|
||
|
|
.drr_node_id = 80,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 4,
|
||
|
|
.flow_level = true,
|
||
|
|
.node_id = 48,
|
||
|
|
.loop_num = 2,
|
||
|
|
.pri_max = 0,
|
||
|
|
.flow_id = 4,
|
||
|
|
.drr_node_id = 20,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 4,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 192,
|
||
|
|
.loop_num = 16,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 48,
|
||
|
|
.drr_node_id = 96,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 4,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 284,
|
||
|
|
.loop_num = 4,
|
||
|
|
.pri_max = 4,
|
||
|
|
.flow_id = 48,
|
||
|
|
.drr_node_id = 96,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 5,
|
||
|
|
.flow_level = true,
|
||
|
|
.node_id = 52,
|
||
|
|
.loop_num = 2,
|
||
|
|
.pri_max = 0,
|
||
|
|
.flow_id = 5,
|
||
|
|
.drr_node_id = 24,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 5,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 208,
|
||
|
|
.loop_num = 16,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 52,
|
||
|
|
.drr_node_id = 112,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 5,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 288,
|
||
|
|
.loop_num = 4,
|
||
|
|
.pri_max = 4,
|
||
|
|
.flow_id = 52,
|
||
|
|
.drr_node_id = 112,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 6,
|
||
|
|
.flow_level = true,
|
||
|
|
.node_id = 56,
|
||
|
|
.loop_num = 2,
|
||
|
|
.pri_max = 0,
|
||
|
|
.flow_id = 6,
|
||
|
|
.drr_node_id = 28,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 6,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 224,
|
||
|
|
.loop_num = 16,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 56,
|
||
|
|
.drr_node_id = 128,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 6,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 292,
|
||
|
|
.loop_num = 4,
|
||
|
|
.pri_max = 4,
|
||
|
|
.flow_id = 56,
|
||
|
|
.drr_node_id = 128,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 7,
|
||
|
|
.flow_level = true,
|
||
|
|
.node_id = 60,
|
||
|
|
.loop_num = 2,
|
||
|
|
.pri_max = 0,
|
||
|
|
.flow_id = 7,
|
||
|
|
.drr_node_id = 32,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 7,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 240,
|
||
|
|
.loop_num = 16,
|
||
|
|
.pri_max = 8,
|
||
|
|
.flow_id = 60,
|
||
|
|
.drr_node_id = 144,
|
||
|
|
},
|
||
|
|
{
|
||
|
|
.port = 7,
|
||
|
|
.flow_level = false,
|
||
|
|
.node_id = 296,
|
||
|
|
.loop_num = 4,
|
||
|
|
.pri_max = 4,
|
||
|
|
.flow_id = 60,
|
||
|
|
.drr_node_id = 144,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
/* The scheduler resource is applied to each PPE port, The resource
|
||
|
|
* includes the unicast & multicast queues, flow nodes and DRR nodes.
|
||
|
|
*/
|
||
|
|
static const struct ppe_port_schedule_resource ppe_scheduler_res[] = {
|
||
|
|
{ .ucastq_start = 0,
|
||
|
|
.ucastq_end = 63,
|
||
|
|
.mcastq_start = 256,
|
||
|
|
.mcastq_end = 271,
|
||
|
|
.flow_id_start = 0,
|
||
|
|
.flow_id_end = 0,
|
||
|
|
.l0node_start = 0,
|
||
|
|
.l0node_end = 7,
|
||
|
|
.l1node_start = 0,
|
||
|
|
.l1node_end = 0,
|
||
|
|
},
|
||
|
|
{ .ucastq_start = 144,
|
||
|
|
.ucastq_end = 159,
|
||
|
|
.mcastq_start = 272,
|
||
|
|
.mcastq_end = 275,
|
||
|
|
.flow_id_start = 36,
|
||
|
|
.flow_id_end = 39,
|
||
|
|
.l0node_start = 48,
|
||
|
|
.l0node_end = 63,
|
||
|
|
.l1node_start = 8,
|
||
|
|
.l1node_end = 11,
|
||
|
|
},
|
||
|
|
{ .ucastq_start = 160,
|
||
|
|
.ucastq_end = 175,
|
||
|
|
.mcastq_start = 276,
|
||
|
|
.mcastq_end = 279,
|
||
|
|
.flow_id_start = 40,
|
||
|
|
.flow_id_end = 43,
|
||
|
|
.l0node_start = 64,
|
||
|
|
.l0node_end = 79,
|
||
|
|
.l1node_start = 12,
|
||
|
|
.l1node_end = 15,
|
||
|
|
},
|
||
|
|
{ .ucastq_start = 176,
|
||
|
|
.ucastq_end = 191,
|
||
|
|
.mcastq_start = 280,
|
||
|
|
.mcastq_end = 283,
|
||
|
|
.flow_id_start = 44,
|
||
|
|
.flow_id_end = 47,
|
||
|
|
.l0node_start = 80,
|
||
|
|
.l0node_end = 95,
|
||
|
|
.l1node_start = 16,
|
||
|
|
.l1node_end = 19,
|
||
|
|
},
|
||
|
|
{ .ucastq_start = 192,
|
||
|
|
.ucastq_end = 207,
|
||
|
|
.mcastq_start = 284,
|
||
|
|
.mcastq_end = 287,
|
||
|
|
.flow_id_start = 48,
|
||
|
|
.flow_id_end = 51,
|
||
|
|
.l0node_start = 96,
|
||
|
|
.l0node_end = 111,
|
||
|
|
.l1node_start = 20,
|
||
|
|
.l1node_end = 23,
|
||
|
|
},
|
||
|
|
{ .ucastq_start = 208,
|
||
|
|
.ucastq_end = 223,
|
||
|
|
.mcastq_start = 288,
|
||
|
|
.mcastq_end = 291,
|
||
|
|
.flow_id_start = 52,
|
||
|
|
.flow_id_end = 55,
|
||
|
|
.l0node_start = 112,
|
||
|
|
.l0node_end = 127,
|
||
|
|
.l1node_start = 24,
|
||
|
|
.l1node_end = 27,
|
||
|
|
},
|
||
|
|
{ .ucastq_start = 224,
|
||
|
|
.ucastq_end = 239,
|
||
|
|
.mcastq_start = 292,
|
||
|
|
.mcastq_end = 295,
|
||
|
|
.flow_id_start = 56,
|
||
|
|
.flow_id_end = 59,
|
||
|
|
.l0node_start = 128,
|
||
|
|
.l0node_end = 143,
|
||
|
|
.l1node_start = 28,
|
||
|
|
.l1node_end = 31,
|
||
|
|
},
|
||
|
|
{ .ucastq_start = 240,
|
||
|
|
.ucastq_end = 255,
|
||
|
|
.mcastq_start = 296,
|
||
|
|
.mcastq_end = 299,
|
||
|
|
.flow_id_start = 60,
|
||
|
|
.flow_id_end = 63,
|
||
|
|
.l0node_start = 144,
|
||
|
|
.l0node_end = 159,
|
||
|
|
.l1node_start = 32,
|
||
|
|
.l1node_end = 35,
|
||
|
|
},
|
||
|
|
{ .ucastq_start = 64,
|
||
|
|
.ucastq_end = 143,
|
||
|
|
.mcastq_start = 0,
|
||
|
|
.mcastq_end = 0,
|
||
|
|
.flow_id_start = 1,
|
||
|
|
.flow_id_end = 35,
|
||
|
|
.l0node_start = 8,
|
||
|
|
.l0node_end = 47,
|
||
|
|
.l1node_start = 1,
|
||
|
|
.l1node_end = 7,
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
/* Set the PPE queue level scheduler configuration. */
|
||
|
|
static int ppe_scheduler_l0_queue_map_set(struct ppe_device *ppe_dev,
|
||
|
|
int node_id, int port,
|
||
|
|
struct ppe_scheduler_cfg scheduler_cfg)
|
||
|
|
{
|
||
|
|
u32 val, reg;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
reg = PPE_L0_FLOW_MAP_TBL_ADDR + node_id * PPE_L0_FLOW_MAP_TBL_INC;
|
||
|
|
val = FIELD_PREP(PPE_L0_FLOW_MAP_TBL_FLOW_ID, scheduler_cfg.flow_id);
|
||
|
|
val |= FIELD_PREP(PPE_L0_FLOW_MAP_TBL_C_PRI, scheduler_cfg.pri);
|
||
|
|
val |= FIELD_PREP(PPE_L0_FLOW_MAP_TBL_E_PRI, scheduler_cfg.pri);
|
||
|
|
val |= FIELD_PREP(PPE_L0_FLOW_MAP_TBL_C_NODE_WT, scheduler_cfg.drr_node_wt);
|
||
|
|
val |= FIELD_PREP(PPE_L0_FLOW_MAP_TBL_E_NODE_WT, scheduler_cfg.drr_node_wt);
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_L0_C_FLOW_CFG_TBL_ADDR +
|
||
|
|
(scheduler_cfg.flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg.pri) *
|
||
|
|
PPE_L0_C_FLOW_CFG_TBL_INC;
|
||
|
|
val = FIELD_PREP(PPE_L0_C_FLOW_CFG_TBL_NODE_ID, scheduler_cfg.drr_node_id);
|
||
|
|
val |= FIELD_PREP(PPE_L0_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT, scheduler_cfg.unit_is_packet);
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_L0_E_FLOW_CFG_TBL_ADDR +
|
||
|
|
(scheduler_cfg.flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg.pri) *
|
||
|
|
PPE_L0_E_FLOW_CFG_TBL_INC;
|
||
|
|
val = FIELD_PREP(PPE_L0_E_FLOW_CFG_TBL_NODE_ID, scheduler_cfg.drr_node_id);
|
||
|
|
val |= FIELD_PREP(PPE_L0_E_FLOW_CFG_TBL_NODE_CREDIT_UNIT, scheduler_cfg.unit_is_packet);
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_L0_FLOW_PORT_MAP_TBL_ADDR + node_id * PPE_L0_FLOW_PORT_MAP_TBL_INC;
|
||
|
|
val = FIELD_PREP(PPE_L0_FLOW_PORT_MAP_TBL_PORT_NUM, port);
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_L0_COMP_CFG_TBL_ADDR + node_id * PPE_L0_COMP_CFG_TBL_INC;
|
||
|
|
val = FIELD_PREP(PPE_L0_COMP_CFG_TBL_NODE_METER_LEN, scheduler_cfg.frame_mode);
|
||
|
|
|
||
|
|
return regmap_update_bits(ppe_dev->regmap, reg,
|
||
|
|
PPE_L0_COMP_CFG_TBL_NODE_METER_LEN,
|
||
|
|
val);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Set the PPE flow level scheduler configuration. */
|
||
|
|
static int ppe_scheduler_l1_queue_map_set(struct ppe_device *ppe_dev,
|
||
|
|
int node_id, int port,
|
||
|
|
struct ppe_scheduler_cfg scheduler_cfg)
|
||
|
|
{
|
||
|
|
u32 val, reg;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
val = FIELD_PREP(PPE_L1_FLOW_MAP_TBL_FLOW_ID, scheduler_cfg.flow_id);
|
||
|
|
val |= FIELD_PREP(PPE_L1_FLOW_MAP_TBL_C_PRI, scheduler_cfg.pri);
|
||
|
|
val |= FIELD_PREP(PPE_L1_FLOW_MAP_TBL_E_PRI, scheduler_cfg.pri);
|
||
|
|
val |= FIELD_PREP(PPE_L1_FLOW_MAP_TBL_C_NODE_WT, scheduler_cfg.drr_node_wt);
|
||
|
|
val |= FIELD_PREP(PPE_L1_FLOW_MAP_TBL_E_NODE_WT, scheduler_cfg.drr_node_wt);
|
||
|
|
reg = PPE_L1_FLOW_MAP_TBL_ADDR + node_id * PPE_L1_FLOW_MAP_TBL_INC;
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
val = FIELD_PREP(PPE_L1_C_FLOW_CFG_TBL_NODE_ID, scheduler_cfg.drr_node_id);
|
||
|
|
val |= FIELD_PREP(PPE_L1_C_FLOW_CFG_TBL_NODE_CREDIT_UNIT, scheduler_cfg.unit_is_packet);
|
||
|
|
reg = PPE_L1_C_FLOW_CFG_TBL_ADDR +
|
||
|
|
(scheduler_cfg.flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg.pri) *
|
||
|
|
PPE_L1_C_FLOW_CFG_TBL_INC;
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
val = FIELD_PREP(PPE_L1_E_FLOW_CFG_TBL_NODE_ID, scheduler_cfg.drr_node_id);
|
||
|
|
val |= FIELD_PREP(PPE_L1_E_FLOW_CFG_TBL_NODE_CREDIT_UNIT, scheduler_cfg.unit_is_packet);
|
||
|
|
reg = PPE_L1_E_FLOW_CFG_TBL_ADDR +
|
||
|
|
(scheduler_cfg.flow_id * PPE_QUEUE_SCH_PRI_NUM + scheduler_cfg.pri) *
|
||
|
|
PPE_L1_E_FLOW_CFG_TBL_INC;
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
val = FIELD_PREP(PPE_L1_FLOW_PORT_MAP_TBL_PORT_NUM, port);
|
||
|
|
reg = PPE_L1_FLOW_PORT_MAP_TBL_ADDR + node_id * PPE_L1_FLOW_PORT_MAP_TBL_INC;
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_L1_COMP_CFG_TBL_ADDR + node_id * PPE_L1_COMP_CFG_TBL_INC;
|
||
|
|
val = FIELD_PREP(PPE_L1_COMP_CFG_TBL_NODE_METER_LEN, scheduler_cfg.frame_mode);
|
||
|
|
|
||
|
|
return regmap_update_bits(ppe_dev->regmap, reg, PPE_L1_COMP_CFG_TBL_NODE_METER_LEN, val);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_queue_scheduler_set - Configure scheduler for PPE hardware queue
|
||
|
|
* @ppe_dev: PPE device
|
||
|
|
* @node_id: PPE queue ID or flow ID
|
||
|
|
* @flow_level: Flow level scheduler or queue level scheduler
|
||
|
|
* @port: PPE port ID set scheduler configuration
|
||
|
|
* @scheduler_cfg: PPE scheduler configuration
|
||
|
|
*
|
||
|
|
* PPE scheduler configuration supports queue level and flow level on
|
||
|
|
* the PPE egress port.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_queue_scheduler_set(struct ppe_device *ppe_dev,
|
||
|
|
int node_id, bool flow_level, int port,
|
||
|
|
struct ppe_scheduler_cfg scheduler_cfg)
|
||
|
|
{
|
||
|
|
if (flow_level)
|
||
|
|
return ppe_scheduler_l1_queue_map_set(ppe_dev, node_id,
|
||
|
|
port, scheduler_cfg);
|
||
|
|
|
||
|
|
return ppe_scheduler_l0_queue_map_set(ppe_dev, node_id,
|
||
|
|
port, scheduler_cfg);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_queue_ucast_base_set - Set PPE unicast queue base ID and profile ID
|
||
|
|
* @ppe_dev: PPE device
|
||
|
|
* @queue_dst: PPE queue destination configuration
|
||
|
|
* @queue_base: PPE queue base ID
|
||
|
|
* @profile_id: Profile ID
|
||
|
|
*
|
||
|
|
* The PPE unicast queue base ID and profile ID are configured based on the
|
||
|
|
* destination port information that can be service code or CPU code or the
|
||
|
|
* destination port.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_queue_ucast_base_set(struct ppe_device *ppe_dev,
|
||
|
|
struct ppe_queue_ucast_dest queue_dst,
|
||
|
|
int queue_base, int profile_id)
|
||
|
|
{
|
||
|
|
int index, profile_size;
|
||
|
|
u32 val, reg;
|
||
|
|
|
||
|
|
profile_size = queue_dst.src_profile << 8;
|
||
|
|
if (queue_dst.service_code_en)
|
||
|
|
index = PPE_QUEUE_BASE_SERVICE_CODE + profile_size +
|
||
|
|
queue_dst.service_code;
|
||
|
|
else if (queue_dst.cpu_code_en)
|
||
|
|
index = PPE_QUEUE_BASE_CPU_CODE + profile_size +
|
||
|
|
queue_dst.cpu_code;
|
||
|
|
else
|
||
|
|
index = profile_size + queue_dst.dest_port;
|
||
|
|
|
||
|
|
val = FIELD_PREP(PPE_UCAST_QUEUE_MAP_TBL_PROFILE_ID, profile_id);
|
||
|
|
val |= FIELD_PREP(PPE_UCAST_QUEUE_MAP_TBL_QUEUE_ID, queue_base);
|
||
|
|
reg = PPE_UCAST_QUEUE_MAP_TBL_ADDR + index * PPE_UCAST_QUEUE_MAP_TBL_INC;
|
||
|
|
|
||
|
|
return regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_queue_ucast_offset_pri_set - Set PPE unicast queue offset based on priority
|
||
|
|
* @ppe_dev: PPE device
|
||
|
|
* @profile_id: Profile ID
|
||
|
|
* @priority: PPE internal priority to be used to set queue offset
|
||
|
|
* @queue_offset: Queue offset used for calculating the destination queue ID
|
||
|
|
*
|
||
|
|
* The PPE unicast queue offset is configured based on the PPE
|
||
|
|
* internal priority.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_queue_ucast_offset_pri_set(struct ppe_device *ppe_dev,
|
||
|
|
int profile_id,
|
||
|
|
int priority,
|
||
|
|
int queue_offset)
|
||
|
|
{
|
||
|
|
u32 val, reg;
|
||
|
|
int index;
|
||
|
|
|
||
|
|
index = (profile_id << 4) + priority;
|
||
|
|
val = FIELD_PREP(PPE_UCAST_PRIORITY_MAP_TBL_CLASS, queue_offset);
|
||
|
|
reg = PPE_UCAST_PRIORITY_MAP_TBL_ADDR + index * PPE_UCAST_PRIORITY_MAP_TBL_INC;
|
||
|
|
|
||
|
|
return regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_queue_ucast_offset_hash_set - Set PPE unicast queue offset based on hash
|
||
|
|
* @ppe_dev: PPE device
|
||
|
|
* @profile_id: Profile ID
|
||
|
|
* @rss_hash: Packet hash value to be used to set queue offset
|
||
|
|
* @queue_offset: Queue offset used for calculating the destination queue ID
|
||
|
|
*
|
||
|
|
* The PPE unicast queue offset is configured based on the RSS hash value.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_queue_ucast_offset_hash_set(struct ppe_device *ppe_dev,
|
||
|
|
int profile_id,
|
||
|
|
int rss_hash,
|
||
|
|
int queue_offset)
|
||
|
|
{
|
||
|
|
u32 val, reg;
|
||
|
|
int index;
|
||
|
|
|
||
|
|
index = (profile_id << 8) + rss_hash;
|
||
|
|
val = FIELD_PREP(PPE_UCAST_HASH_MAP_TBL_HASH, queue_offset);
|
||
|
|
reg = PPE_UCAST_HASH_MAP_TBL_ADDR + index * PPE_UCAST_HASH_MAP_TBL_INC;
|
||
|
|
|
||
|
|
return regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_port_resource_get - Get PPE resource per port
|
||
|
|
* @ppe_dev: PPE device
|
||
|
|
* @port: PPE port
|
||
|
|
* @type: Resource type
|
||
|
|
* @res_start: Resource start ID returned
|
||
|
|
* @res_end: Resource end ID returned
|
||
|
|
*
|
||
|
|
* PPE resource is assigned per PPE port, which is acquired for QoS scheduler.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_port_resource_get(struct ppe_device *ppe_dev, int port,
|
||
|
|
enum ppe_resource_type type,
|
||
|
|
int *res_start, int *res_end)
|
||
|
|
{
|
||
|
|
struct ppe_port_schedule_resource res;
|
||
|
|
|
||
|
|
/* The reserved resource with the maximum port ID of PPE is
|
||
|
|
* also allowed to be acquired.
|
||
|
|
*/
|
||
|
|
if (port > ppe_dev->num_ports)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
res = ppe_scheduler_res[port];
|
||
|
|
switch (type) {
|
||
|
|
case PPE_RES_UCAST:
|
||
|
|
*res_start = res.ucastq_start;
|
||
|
|
*res_end = res.ucastq_end;
|
||
|
|
break;
|
||
|
|
case PPE_RES_MCAST:
|
||
|
|
*res_start = res.mcastq_start;
|
||
|
|
*res_end = res.mcastq_end;
|
||
|
|
break;
|
||
|
|
case PPE_RES_FLOW_ID:
|
||
|
|
*res_start = res.flow_id_start;
|
||
|
|
*res_end = res.flow_id_end;
|
||
|
|
break;
|
||
|
|
case PPE_RES_L0_NODE:
|
||
|
|
*res_start = res.l0node_start;
|
||
|
|
*res_end = res.l0node_end;
|
||
|
|
break;
|
||
|
|
case PPE_RES_L1_NODE:
|
||
|
|
*res_start = res.l1node_start;
|
||
|
|
*res_end = res.l1node_end;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_sc_config_set - Set PPE service code configuration
|
||
|
|
* @ppe_dev: PPE device
|
||
|
|
* @sc: Service ID, 0-255 supported by PPE
|
||
|
|
* @cfg: Service code configuration
|
||
|
|
*
|
||
|
|
* PPE service code is used by the PPE during its packet processing stages,
|
||
|
|
* to perform or bypass certain selected packet operations on the packet.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_sc_config_set(struct ppe_device *ppe_dev, int sc, struct ppe_sc_cfg cfg)
|
||
|
|
{
|
||
|
|
u32 val, reg, servcode_val[2] = {};
|
||
|
|
unsigned long bitmap_value;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
val = FIELD_PREP(PPE_IN_L2_SERVICE_TBL_DST_PORT_ID_VALID, cfg.dest_port_valid);
|
||
|
|
val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_DST_PORT_ID, cfg.dest_port);
|
||
|
|
val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_DST_DIRECTION, cfg.is_src);
|
||
|
|
|
||
|
|
bitmap_value = bitmap_read(cfg.bitmaps.egress, 0, PPE_SC_BYPASS_EGRESS_SIZE);
|
||
|
|
val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_DST_BYPASS_BITMAP, bitmap_value);
|
||
|
|
val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_RX_CNT_EN,
|
||
|
|
test_bit(PPE_SC_BYPASS_COUNTER_RX, cfg.bitmaps.counter));
|
||
|
|
val |= FIELD_PREP(PPE_IN_L2_SERVICE_TBL_TX_CNT_EN,
|
||
|
|
test_bit(PPE_SC_BYPASS_COUNTER_TX, cfg.bitmaps.counter));
|
||
|
|
reg = PPE_IN_L2_SERVICE_TBL_ADDR + PPE_IN_L2_SERVICE_TBL_INC * sc;
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
bitmap_value = bitmap_read(cfg.bitmaps.ingress, 0, PPE_SC_BYPASS_INGRESS_SIZE);
|
||
|
|
PPE_SERVICE_SET_BYPASS_BITMAP(servcode_val, bitmap_value);
|
||
|
|
PPE_SERVICE_SET_RX_CNT_EN(servcode_val,
|
||
|
|
test_bit(PPE_SC_BYPASS_COUNTER_RX_VLAN, cfg.bitmaps.counter));
|
||
|
|
reg = PPE_SERVICE_TBL_ADDR + PPE_SERVICE_TBL_INC * sc;
|
||
|
|
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
servcode_val, ARRAY_SIZE(servcode_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_EG_SERVICE_TBL_ADDR + PPE_EG_SERVICE_TBL_INC * sc;
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
servcode_val, ARRAY_SIZE(servcode_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
PPE_EG_SERVICE_SET_NEXT_SERVCODE(servcode_val, cfg.next_service_code);
|
||
|
|
PPE_EG_SERVICE_SET_UPDATE_ACTION(servcode_val, cfg.eip_field_update_bitmap);
|
||
|
|
PPE_EG_SERVICE_SET_HW_SERVICE(servcode_val, cfg.eip_hw_service);
|
||
|
|
PPE_EG_SERVICE_SET_OFFSET_SEL(servcode_val, cfg.eip_offset_sel);
|
||
|
|
PPE_EG_SERVICE_SET_TX_CNT_EN(servcode_val,
|
||
|
|
test_bit(PPE_SC_BYPASS_COUNTER_TX_VLAN, cfg.bitmaps.counter));
|
||
|
|
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
servcode_val, ARRAY_SIZE(servcode_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
bitmap_value = bitmap_read(cfg.bitmaps.tunnel, 0, PPE_SC_BYPASS_TUNNEL_SIZE);
|
||
|
|
val = FIELD_PREP(PPE_TL_SERVICE_TBL_BYPASS_BITMAP, bitmap_value);
|
||
|
|
reg = PPE_TL_SERVICE_TBL_ADDR + PPE_TL_SERVICE_TBL_INC * sc;
|
||
|
|
|
||
|
|
return regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_counter_enable_set - Set PPE port counter enabled
|
||
|
|
* @ppe_dev: PPE device
|
||
|
|
* @port: PPE port ID
|
||
|
|
*
|
||
|
|
* Enable PPE counters on the given port for the unicast packet, multicast
|
||
|
|
* packet and VLAN packet received and transmitted by PPE.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_counter_enable_set(struct ppe_device *ppe_dev, int port)
|
||
|
|
{
|
||
|
|
u32 reg, mru_mtu_val[3];
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
reg = PPE_MRU_MTU_CTRL_TBL_ADDR + PPE_MRU_MTU_CTRL_TBL_INC * port;
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
PPE_MRU_MTU_CTRL_SET_RX_CNT_EN(mru_mtu_val, true);
|
||
|
|
PPE_MRU_MTU_CTRL_SET_TX_CNT_EN(mru_mtu_val, true);
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_MC_MTU_CTRL_TBL_ADDR + PPE_MC_MTU_CTRL_TBL_INC * port;
|
||
|
|
ret = regmap_set_bits(ppe_dev->regmap, reg, PPE_MC_MTU_CTRL_TBL_TX_CNT_EN);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_PORT_EG_VLAN_TBL_ADDR + PPE_PORT_EG_VLAN_TBL_INC * port;
|
||
|
|
|
||
|
|
return regmap_set_bits(ppe_dev->regmap, reg, PPE_PORT_EG_VLAN_TBL_TX_COUNTING_EN);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ppe_rss_hash_ipv4_config(struct ppe_device *ppe_dev, int index,
|
||
|
|
struct ppe_rss_hash_cfg cfg)
|
||
|
|
{
|
||
|
|
u32 reg, val;
|
||
|
|
|
||
|
|
switch (index) {
|
||
|
|
case 0:
|
||
|
|
val = cfg.hash_sip_mix[0];
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
val = cfg.hash_dip_mix[0];
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
val = cfg.hash_protocol_mix;
|
||
|
|
break;
|
||
|
|
case 3:
|
||
|
|
val = cfg.hash_dport_mix;
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
val = cfg.hash_sport_mix;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
reg = PPE_RSS_HASH_MIX_IPV4_ADDR + index * PPE_RSS_HASH_MIX_IPV4_INC;
|
||
|
|
|
||
|
|
return regmap_update_bits(ppe_dev->regmap, reg,
|
||
|
|
PPE_RSS_HASH_MIX_IPV4_VAL,
|
||
|
|
FIELD_PREP(PPE_RSS_HASH_MIX_IPV4_VAL, val));
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ppe_rss_hash_ipv6_config(struct ppe_device *ppe_dev, int index,
|
||
|
|
struct ppe_rss_hash_cfg cfg)
|
||
|
|
{
|
||
|
|
u32 reg, val;
|
||
|
|
|
||
|
|
switch (index) {
|
||
|
|
case 0 ... 3:
|
||
|
|
val = cfg.hash_sip_mix[index];
|
||
|
|
break;
|
||
|
|
case 4 ... 7:
|
||
|
|
val = cfg.hash_dip_mix[index - 4];
|
||
|
|
break;
|
||
|
|
case 8:
|
||
|
|
val = cfg.hash_protocol_mix;
|
||
|
|
break;
|
||
|
|
case 9:
|
||
|
|
val = cfg.hash_dport_mix;
|
||
|
|
break;
|
||
|
|
case 10:
|
||
|
|
val = cfg.hash_sport_mix;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
reg = PPE_RSS_HASH_MIX_ADDR + index * PPE_RSS_HASH_MIX_INC;
|
||
|
|
|
||
|
|
return regmap_update_bits(ppe_dev->regmap, reg,
|
||
|
|
PPE_RSS_HASH_MIX_VAL,
|
||
|
|
FIELD_PREP(PPE_RSS_HASH_MIX_VAL, val));
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_rss_hash_config_set - Configure the PPE hash settings for the packet received.
|
||
|
|
* @ppe_dev: PPE device.
|
||
|
|
* @mode: Configure RSS hash for the packet type IPv4 and IPv6.
|
||
|
|
* @cfg: RSS hash configuration.
|
||
|
|
*
|
||
|
|
* PPE RSS hash settings are configured for the packet type IPv4 and IPv6.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_rss_hash_config_set(struct ppe_device *ppe_dev, int mode,
|
||
|
|
struct ppe_rss_hash_cfg cfg)
|
||
|
|
{
|
||
|
|
u32 val, reg;
|
||
|
|
int i, ret;
|
||
|
|
|
||
|
|
if (mode & PPE_RSS_HASH_MODE_IPV4) {
|
||
|
|
val = FIELD_PREP(PPE_RSS_HASH_MASK_IPV4_HASH_MASK, cfg.hash_mask);
|
||
|
|
val |= FIELD_PREP(PPE_RSS_HASH_MASK_IPV4_FRAGMENT, cfg.hash_fragment_mode);
|
||
|
|
ret = regmap_write(ppe_dev->regmap, PPE_RSS_HASH_MASK_IPV4_ADDR, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
val = FIELD_PREP(PPE_RSS_HASH_SEED_IPV4_VAL, cfg.hash_seed);
|
||
|
|
ret = regmap_write(ppe_dev->regmap, PPE_RSS_HASH_SEED_IPV4_ADDR, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
for (i = 0; i < PPE_RSS_HASH_MIX_IPV4_ENTRIES; i++) {
|
||
|
|
ret = ppe_rss_hash_ipv4_config(ppe_dev, i, cfg);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < PPE_RSS_HASH_FIN_IPV4_ENTRIES; i++) {
|
||
|
|
val = FIELD_PREP(PPE_RSS_HASH_FIN_IPV4_INNER, cfg.hash_fin_inner[i]);
|
||
|
|
val |= FIELD_PREP(PPE_RSS_HASH_FIN_IPV4_OUTER, cfg.hash_fin_outer[i]);
|
||
|
|
reg = PPE_RSS_HASH_FIN_IPV4_ADDR + i * PPE_RSS_HASH_FIN_IPV4_INC;
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (mode & PPE_RSS_HASH_MODE_IPV6) {
|
||
|
|
val = FIELD_PREP(PPE_RSS_HASH_MASK_HASH_MASK, cfg.hash_mask);
|
||
|
|
val |= FIELD_PREP(PPE_RSS_HASH_MASK_FRAGMENT, cfg.hash_fragment_mode);
|
||
|
|
ret = regmap_write(ppe_dev->regmap, PPE_RSS_HASH_MASK_ADDR, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
val = FIELD_PREP(PPE_RSS_HASH_SEED_VAL, cfg.hash_seed);
|
||
|
|
ret = regmap_write(ppe_dev->regmap, PPE_RSS_HASH_SEED_ADDR, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
for (i = 0; i < PPE_RSS_HASH_MIX_ENTRIES; i++) {
|
||
|
|
ret = ppe_rss_hash_ipv6_config(ppe_dev, i, cfg);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < PPE_RSS_HASH_FIN_ENTRIES; i++) {
|
||
|
|
val = FIELD_PREP(PPE_RSS_HASH_FIN_INNER, cfg.hash_fin_inner[i]);
|
||
|
|
val |= FIELD_PREP(PPE_RSS_HASH_FIN_OUTER, cfg.hash_fin_outer[i]);
|
||
|
|
reg = PPE_RSS_HASH_FIN_ADDR + i * PPE_RSS_HASH_FIN_INC;
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ppe_ring_queue_map_set - Set the PPE queue to Ethernet DMA ring mapping
|
||
|
|
* @ppe_dev: PPE device
|
||
|
|
* @ring_id: Ethernet DMA ring ID
|
||
|
|
* @queue_map: Bit map of queue IDs to given Ethernet DMA ring
|
||
|
|
*
|
||
|
|
* Configure the mapping from a set of PPE queues to a given Ethernet DMA ring.
|
||
|
|
*
|
||
|
|
* Return: 0 on success, negative error code on failure.
|
||
|
|
*/
|
||
|
|
int ppe_ring_queue_map_set(struct ppe_device *ppe_dev, int ring_id, u32 *queue_map)
|
||
|
|
{
|
||
|
|
u32 reg, queue_bitmap_val[PPE_RING_TO_QUEUE_BITMAP_WORD_CNT];
|
||
|
|
|
||
|
|
memcpy(queue_bitmap_val, queue_map, sizeof(queue_bitmap_val));
|
||
|
|
reg = PPE_RING_Q_MAP_TBL_ADDR + PPE_RING_Q_MAP_TBL_INC * ring_id;
|
||
|
|
|
||
|
|
return regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
queue_bitmap_val,
|
||
|
|
ARRAY_SIZE(queue_bitmap_val));
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ppe_config_bm_threshold(struct ppe_device *ppe_dev, int bm_port_id,
|
||
|
|
const struct ppe_bm_port_config port_cfg)
|
||
|
|
{
|
||
|
|
u32 reg, val, bm_fc_val[2];
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
reg = PPE_BM_PORT_FC_CFG_TBL_ADDR + PPE_BM_PORT_FC_CFG_TBL_INC * bm_port_id;
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
bm_fc_val, ARRAY_SIZE(bm_fc_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* Configure BM flow control related threshold. */
|
||
|
|
PPE_BM_PORT_FC_SET_WEIGHT(bm_fc_val, port_cfg.weight);
|
||
|
|
PPE_BM_PORT_FC_SET_RESUME_OFFSET(bm_fc_val, port_cfg.resume_offset);
|
||
|
|
PPE_BM_PORT_FC_SET_RESUME_THRESHOLD(bm_fc_val, port_cfg.resume_ceil);
|
||
|
|
PPE_BM_PORT_FC_SET_DYNAMIC(bm_fc_val, port_cfg.dynamic);
|
||
|
|
PPE_BM_PORT_FC_SET_REACT_LIMIT(bm_fc_val, port_cfg.in_fly_buf);
|
||
|
|
PPE_BM_PORT_FC_SET_PRE_ALLOC(bm_fc_val, port_cfg.pre_alloc);
|
||
|
|
|
||
|
|
/* Configure low/high bits of the ceiling for the BM port. */
|
||
|
|
val = FIELD_GET(GENMASK(2, 0), port_cfg.ceil);
|
||
|
|
PPE_BM_PORT_FC_SET_CEILING_LOW(bm_fc_val, val);
|
||
|
|
val = FIELD_GET(GENMASK(10, 3), port_cfg.ceil);
|
||
|
|
PPE_BM_PORT_FC_SET_CEILING_HIGH(bm_fc_val, val);
|
||
|
|
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
bm_fc_val, ARRAY_SIZE(bm_fc_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* Assign the default group ID 0 to the BM port. */
|
||
|
|
val = FIELD_PREP(PPE_BM_PORT_GROUP_ID_SHARED_GROUP_ID, 0);
|
||
|
|
reg = PPE_BM_PORT_GROUP_ID_ADDR + PPE_BM_PORT_GROUP_ID_INC * bm_port_id;
|
||
|
|
ret = regmap_update_bits(ppe_dev->regmap, reg,
|
||
|
|
PPE_BM_PORT_GROUP_ID_SHARED_GROUP_ID,
|
||
|
|
val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* Enable BM port flow control. */
|
||
|
|
reg = PPE_BM_PORT_FC_MODE_ADDR + PPE_BM_PORT_FC_MODE_INC * bm_port_id;
|
||
|
|
|
||
|
|
return regmap_set_bits(ppe_dev->regmap, reg, PPE_BM_PORT_FC_MODE_EN);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Configure the buffer threshold for the port flow control function. */
|
||
|
|
static int ppe_config_bm(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
const struct ppe_bm_port_config *port_cfg;
|
||
|
|
unsigned int i, bm_port_id, port_cfg_cnt;
|
||
|
|
u32 reg, val;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* Configure the allocated buffer number only for group 0.
|
||
|
|
* The buffer number of group 1-3 is already cleared to 0
|
||
|
|
* after PPE reset during the probe of PPE driver.
|
||
|
|
*/
|
||
|
|
reg = PPE_BM_SHARED_GROUP_CFG_ADDR;
|
||
|
|
val = FIELD_PREP(PPE_BM_SHARED_GROUP_CFG_SHARED_LIMIT,
|
||
|
|
ipq9574_ppe_bm_group_config);
|
||
|
|
ret = regmap_update_bits(ppe_dev->regmap, reg,
|
||
|
|
PPE_BM_SHARED_GROUP_CFG_SHARED_LIMIT,
|
||
|
|
val);
|
||
|
|
if (ret)
|
||
|
|
goto bm_config_fail;
|
||
|
|
|
||
|
|
/* Configure buffer thresholds for the BM ports. */
|
||
|
|
port_cfg = ipq9574_ppe_bm_port_config;
|
||
|
|
port_cfg_cnt = ARRAY_SIZE(ipq9574_ppe_bm_port_config);
|
||
|
|
for (i = 0; i < port_cfg_cnt; i++) {
|
||
|
|
for (bm_port_id = port_cfg[i].port_id_start;
|
||
|
|
bm_port_id <= port_cfg[i].port_id_end; bm_port_id++) {
|
||
|
|
ret = ppe_config_bm_threshold(ppe_dev, bm_port_id,
|
||
|
|
port_cfg[i]);
|
||
|
|
if (ret)
|
||
|
|
goto bm_config_fail;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
bm_config_fail:
|
||
|
|
dev_err(ppe_dev->dev, "PPE BM config error %d\n", ret);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Configure PPE hardware queue depth, which is decided by the threshold
|
||
|
|
* of queue.
|
||
|
|
*/
|
||
|
|
static int ppe_config_qm(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
const struct ppe_qm_queue_config *queue_cfg;
|
||
|
|
int ret, i, queue_id, queue_cfg_count;
|
||
|
|
u32 reg, multicast_queue_cfg[5];
|
||
|
|
u32 unicast_queue_cfg[4];
|
||
|
|
u32 group_cfg[3];
|
||
|
|
|
||
|
|
/* Assign the buffer number to the group 0 by default. */
|
||
|
|
reg = PPE_AC_GRP_CFG_TBL_ADDR;
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
group_cfg, ARRAY_SIZE(group_cfg));
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
|
||
|
|
PPE_AC_GRP_SET_BUF_LIMIT(group_cfg, ipq9574_ppe_qm_group_config);
|
||
|
|
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
group_cfg, ARRAY_SIZE(group_cfg));
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
|
||
|
|
queue_cfg = ipq9574_ppe_qm_queue_config;
|
||
|
|
queue_cfg_count = ARRAY_SIZE(ipq9574_ppe_qm_queue_config);
|
||
|
|
for (i = 0; i < queue_cfg_count; i++) {
|
||
|
|
queue_id = queue_cfg[i].queue_start;
|
||
|
|
|
||
|
|
/* Configure threshold for dropping packets separately for
|
||
|
|
* unicast and multicast PPE queues.
|
||
|
|
*/
|
||
|
|
while (queue_id <= queue_cfg[i].queue_end) {
|
||
|
|
if (queue_id < PPE_AC_UNICAST_QUEUE_CFG_TBL_ENTRIES) {
|
||
|
|
reg = PPE_AC_UNICAST_QUEUE_CFG_TBL_ADDR +
|
||
|
|
PPE_AC_UNICAST_QUEUE_CFG_TBL_INC * queue_id;
|
||
|
|
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
unicast_queue_cfg,
|
||
|
|
ARRAY_SIZE(unicast_queue_cfg));
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
|
||
|
|
PPE_AC_UNICAST_QUEUE_SET_EN(unicast_queue_cfg, true);
|
||
|
|
PPE_AC_UNICAST_QUEUE_SET_GRP_ID(unicast_queue_cfg, 0);
|
||
|
|
PPE_AC_UNICAST_QUEUE_SET_PRE_LIMIT(unicast_queue_cfg,
|
||
|
|
queue_cfg[i].prealloc_buf);
|
||
|
|
PPE_AC_UNICAST_QUEUE_SET_DYNAMIC(unicast_queue_cfg,
|
||
|
|
queue_cfg[i].dynamic);
|
||
|
|
PPE_AC_UNICAST_QUEUE_SET_WEIGHT(unicast_queue_cfg,
|
||
|
|
queue_cfg[i].weight);
|
||
|
|
PPE_AC_UNICAST_QUEUE_SET_THRESHOLD(unicast_queue_cfg,
|
||
|
|
queue_cfg[i].ceil);
|
||
|
|
PPE_AC_UNICAST_QUEUE_SET_GRN_RESUME(unicast_queue_cfg,
|
||
|
|
queue_cfg[i].resume_offset);
|
||
|
|
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
unicast_queue_cfg,
|
||
|
|
ARRAY_SIZE(unicast_queue_cfg));
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
} else {
|
||
|
|
reg = PPE_AC_MULTICAST_QUEUE_CFG_TBL_ADDR +
|
||
|
|
PPE_AC_MULTICAST_QUEUE_CFG_TBL_INC * queue_id;
|
||
|
|
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
multicast_queue_cfg,
|
||
|
|
ARRAY_SIZE(multicast_queue_cfg));
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
|
||
|
|
PPE_AC_MULTICAST_QUEUE_SET_EN(multicast_queue_cfg, true);
|
||
|
|
PPE_AC_MULTICAST_QUEUE_SET_GRN_GRP_ID(multicast_queue_cfg, 0);
|
||
|
|
PPE_AC_MULTICAST_QUEUE_SET_GRN_PRE_LIMIT(multicast_queue_cfg,
|
||
|
|
queue_cfg[i].prealloc_buf);
|
||
|
|
PPE_AC_MULTICAST_QUEUE_SET_GRN_THRESHOLD(multicast_queue_cfg,
|
||
|
|
queue_cfg[i].ceil);
|
||
|
|
PPE_AC_MULTICAST_QUEUE_SET_GRN_RESUME(multicast_queue_cfg,
|
||
|
|
queue_cfg[i].resume_offset);
|
||
|
|
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
multicast_queue_cfg,
|
||
|
|
ARRAY_SIZE(multicast_queue_cfg));
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Enable enqueue. */
|
||
|
|
reg = PPE_ENQ_OPR_TBL_ADDR + PPE_ENQ_OPR_TBL_INC * queue_id;
|
||
|
|
ret = regmap_clear_bits(ppe_dev->regmap, reg,
|
||
|
|
PPE_ENQ_OPR_TBL_ENQ_DISABLE);
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
|
||
|
|
/* Enable dequeue. */
|
||
|
|
reg = PPE_DEQ_OPR_TBL_ADDR + PPE_DEQ_OPR_TBL_INC * queue_id;
|
||
|
|
ret = regmap_clear_bits(ppe_dev->regmap, reg,
|
||
|
|
PPE_DEQ_OPR_TBL_DEQ_DISABLE);
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
|
||
|
|
queue_id++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Enable queue counter for all PPE hardware queues. */
|
||
|
|
ret = regmap_set_bits(ppe_dev->regmap, PPE_EG_BRIDGE_CONFIG_ADDR,
|
||
|
|
PPE_EG_BRIDGE_CONFIG_QUEUE_CNT_EN);
|
||
|
|
if (ret)
|
||
|
|
goto qm_config_fail;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
qm_config_fail:
|
||
|
|
dev_err(ppe_dev->dev, "PPE QM config error %d\n", ret);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int ppe_node_scheduler_config(struct ppe_device *ppe_dev,
|
||
|
|
const struct ppe_scheduler_port_config config)
|
||
|
|
{
|
||
|
|
struct ppe_scheduler_cfg sch_cfg;
|
||
|
|
int ret, i;
|
||
|
|
|
||
|
|
for (i = 0; i < config.loop_num; i++) {
|
||
|
|
if (!config.pri_max) {
|
||
|
|
/* Round robin scheduler without priority. */
|
||
|
|
sch_cfg.flow_id = config.flow_id;
|
||
|
|
sch_cfg.pri = 0;
|
||
|
|
sch_cfg.drr_node_id = config.drr_node_id;
|
||
|
|
} else {
|
||
|
|
sch_cfg.flow_id = config.flow_id + (i / config.pri_max);
|
||
|
|
sch_cfg.pri = i % config.pri_max;
|
||
|
|
sch_cfg.drr_node_id = config.drr_node_id + i;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Scheduler weight, must be more than 0. */
|
||
|
|
sch_cfg.drr_node_wt = 1;
|
||
|
|
/* Byte based to be scheduled. */
|
||
|
|
sch_cfg.unit_is_packet = false;
|
||
|
|
/* Frame + CRC calculated. */
|
||
|
|
sch_cfg.frame_mode = PPE_SCH_WITH_FRAME_CRC;
|
||
|
|
|
||
|
|
ret = ppe_queue_scheduler_set(ppe_dev, config.node_id + i,
|
||
|
|
config.flow_level,
|
||
|
|
config.port,
|
||
|
|
sch_cfg);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Initialize scheduler settings for PPE buffer utilization and dispatching
|
||
|
|
* packet on PPE queue.
|
||
|
|
*/
|
||
|
|
static int ppe_config_scheduler(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
const struct ppe_scheduler_port_config *port_cfg;
|
||
|
|
const struct ppe_scheduler_qm_config *qm_cfg;
|
||
|
|
const struct ppe_scheduler_bm_config *bm_cfg;
|
||
|
|
int ret, i, count;
|
||
|
|
u32 val, reg;
|
||
|
|
|
||
|
|
count = ARRAY_SIZE(ipq9574_ppe_sch_bm_config);
|
||
|
|
bm_cfg = ipq9574_ppe_sch_bm_config;
|
||
|
|
|
||
|
|
/* Configure the depth of BM scheduler entries. */
|
||
|
|
val = FIELD_PREP(PPE_BM_SCH_CTRL_SCH_DEPTH, count);
|
||
|
|
val |= FIELD_PREP(PPE_BM_SCH_CTRL_SCH_OFFSET, 0);
|
||
|
|
val |= FIELD_PREP(PPE_BM_SCH_CTRL_SCH_EN, 1);
|
||
|
|
|
||
|
|
ret = regmap_write(ppe_dev->regmap, PPE_BM_SCH_CTRL_ADDR, val);
|
||
|
|
if (ret)
|
||
|
|
goto sch_config_fail;
|
||
|
|
|
||
|
|
/* Configure each BM scheduler entry with the valid ingress port and
|
||
|
|
* egress port, the second port takes effect when the specified port
|
||
|
|
* is in the inactive state.
|
||
|
|
*/
|
||
|
|
for (i = 0; i < count; i++) {
|
||
|
|
val = FIELD_PREP(PPE_BM_SCH_CFG_TBL_VALID, bm_cfg[i].valid);
|
||
|
|
val |= FIELD_PREP(PPE_BM_SCH_CFG_TBL_DIR, bm_cfg[i].dir);
|
||
|
|
val |= FIELD_PREP(PPE_BM_SCH_CFG_TBL_PORT_NUM, bm_cfg[i].port);
|
||
|
|
val |= FIELD_PREP(PPE_BM_SCH_CFG_TBL_SECOND_PORT_VALID,
|
||
|
|
bm_cfg[i].backup_port_valid);
|
||
|
|
val |= FIELD_PREP(PPE_BM_SCH_CFG_TBL_SECOND_PORT,
|
||
|
|
bm_cfg[i].backup_port);
|
||
|
|
|
||
|
|
reg = PPE_BM_SCH_CFG_TBL_ADDR + i * PPE_BM_SCH_CFG_TBL_INC;
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
goto sch_config_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
count = ARRAY_SIZE(ipq9574_ppe_sch_qm_config);
|
||
|
|
qm_cfg = ipq9574_ppe_sch_qm_config;
|
||
|
|
|
||
|
|
/* Configure the depth of QM scheduler entries. */
|
||
|
|
val = FIELD_PREP(PPE_PSCH_SCH_DEPTH_CFG_SCH_DEPTH, count);
|
||
|
|
ret = regmap_write(ppe_dev->regmap, PPE_PSCH_SCH_DEPTH_CFG_ADDR, val);
|
||
|
|
if (ret)
|
||
|
|
goto sch_config_fail;
|
||
|
|
|
||
|
|
/* Configure each QM scheduler entry with enqueue port and dequeue
|
||
|
|
* port, the second port takes effect when the specified dequeue
|
||
|
|
* port is in the inactive port.
|
||
|
|
*/
|
||
|
|
for (i = 0; i < count; i++) {
|
||
|
|
val = FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_ENS_PORT_BITMAP,
|
||
|
|
qm_cfg[i].ensch_port_bmp);
|
||
|
|
val |= FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_ENS_PORT,
|
||
|
|
qm_cfg[i].ensch_port);
|
||
|
|
val |= FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_DES_PORT,
|
||
|
|
qm_cfg[i].desch_port);
|
||
|
|
val |= FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_DES_SECOND_PORT_EN,
|
||
|
|
qm_cfg[i].desch_backup_port_valid);
|
||
|
|
val |= FIELD_PREP(PPE_PSCH_SCH_CFG_TBL_DES_SECOND_PORT,
|
||
|
|
qm_cfg[i].desch_backup_port);
|
||
|
|
|
||
|
|
reg = PPE_PSCH_SCH_CFG_TBL_ADDR + i * PPE_PSCH_SCH_CFG_TBL_INC;
|
||
|
|
ret = regmap_write(ppe_dev->regmap, reg, val);
|
||
|
|
if (ret)
|
||
|
|
goto sch_config_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
count = ARRAY_SIZE(ppe_port_sch_config);
|
||
|
|
port_cfg = ppe_port_sch_config;
|
||
|
|
|
||
|
|
/* Configure scheduler per PPE queue or flow. */
|
||
|
|
for (i = 0; i < count; i++) {
|
||
|
|
if (port_cfg[i].port >= ppe_dev->num_ports)
|
||
|
|
break;
|
||
|
|
|
||
|
|
ret = ppe_node_scheduler_config(ppe_dev, port_cfg[i]);
|
||
|
|
if (ret)
|
||
|
|
goto sch_config_fail;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
sch_config_fail:
|
||
|
|
dev_err(ppe_dev->dev, "PPE scheduler arbitration config error %d\n", ret);
|
||
|
|
return ret;
|
||
|
|
};
|
||
|
|
|
||
|
|
/* Configure PPE queue destination of each PPE port. */
|
||
|
|
static int ppe_queue_dest_init(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
int ret, port_id, index, q_base, q_offset, res_start, res_end, pri_max;
|
||
|
|
struct ppe_queue_ucast_dest queue_dst;
|
||
|
|
|
||
|
|
for (port_id = 0; port_id < ppe_dev->num_ports; port_id++) {
|
||
|
|
memset(&queue_dst, 0, sizeof(queue_dst));
|
||
|
|
|
||
|
|
ret = ppe_port_resource_get(ppe_dev, port_id, PPE_RES_UCAST,
|
||
|
|
&res_start, &res_end);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
q_base = res_start;
|
||
|
|
queue_dst.dest_port = port_id;
|
||
|
|
|
||
|
|
/* Configure queue base ID and profile ID that is same as
|
||
|
|
* physical port ID.
|
||
|
|
*/
|
||
|
|
ret = ppe_queue_ucast_base_set(ppe_dev, queue_dst,
|
||
|
|
q_base, port_id);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* Queue priority range supported by each PPE port */
|
||
|
|
ret = ppe_port_resource_get(ppe_dev, port_id, PPE_RES_L0_NODE,
|
||
|
|
&res_start, &res_end);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
pri_max = res_end - res_start;
|
||
|
|
|
||
|
|
/* Redirect ARP reply packet with the max priority on CPU port,
|
||
|
|
* which keeps the ARP reply directed to CPU (CPU code is 101)
|
||
|
|
* with highest priority queue of EDMA.
|
||
|
|
*/
|
||
|
|
if (port_id == 0) {
|
||
|
|
memset(&queue_dst, 0, sizeof(queue_dst));
|
||
|
|
|
||
|
|
queue_dst.cpu_code_en = true;
|
||
|
|
queue_dst.cpu_code = 101;
|
||
|
|
ret = ppe_queue_ucast_base_set(ppe_dev, queue_dst,
|
||
|
|
q_base + pri_max,
|
||
|
|
0);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Initialize the queue offset of internal priority. */
|
||
|
|
for (index = 0; index < PPE_QUEUE_INTER_PRI_NUM; index++) {
|
||
|
|
q_offset = index > pri_max ? pri_max : index;
|
||
|
|
|
||
|
|
ret = ppe_queue_ucast_offset_pri_set(ppe_dev, port_id,
|
||
|
|
index, q_offset);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Initialize the queue offset of RSS hash as 0 to avoid the
|
||
|
|
* random hardware value that will lead to the unexpected
|
||
|
|
* destination queue generated.
|
||
|
|
*/
|
||
|
|
for (index = 0; index < PPE_QUEUE_HASH_NUM; index++) {
|
||
|
|
ret = ppe_queue_ucast_offset_hash_set(ppe_dev, port_id,
|
||
|
|
index, 0);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Initialize the service code 1 used by CPU port. */
|
||
|
|
static int ppe_servcode_init(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
struct ppe_sc_cfg sc_cfg = {};
|
||
|
|
|
||
|
|
bitmap_zero(sc_cfg.bitmaps.counter, PPE_SC_BYPASS_COUNTER_SIZE);
|
||
|
|
bitmap_zero(sc_cfg.bitmaps.tunnel, PPE_SC_BYPASS_TUNNEL_SIZE);
|
||
|
|
|
||
|
|
bitmap_fill(sc_cfg.bitmaps.ingress, PPE_SC_BYPASS_INGRESS_SIZE);
|
||
|
|
clear_bit(PPE_SC_BYPASS_INGRESS_FAKE_MAC_HEADER, sc_cfg.bitmaps.ingress);
|
||
|
|
clear_bit(PPE_SC_BYPASS_INGRESS_SERVICE_CODE, sc_cfg.bitmaps.ingress);
|
||
|
|
clear_bit(PPE_SC_BYPASS_INGRESS_FAKE_L2_PROTO, sc_cfg.bitmaps.ingress);
|
||
|
|
|
||
|
|
bitmap_fill(sc_cfg.bitmaps.egress, PPE_SC_BYPASS_EGRESS_SIZE);
|
||
|
|
clear_bit(PPE_SC_BYPASS_EGRESS_ACL_POST_ROUTING_CHECK, sc_cfg.bitmaps.egress);
|
||
|
|
|
||
|
|
return ppe_sc_config_set(ppe_dev, PPE_EDMA_SC_BYPASS_ID, sc_cfg);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Initialize PPE port configurations. */
|
||
|
|
static int ppe_port_config_init(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
u32 reg, val, mru_mtu_val[3];
|
||
|
|
int i, ret;
|
||
|
|
|
||
|
|
/* MTU and MRU settings are not required for CPU port 0. */
|
||
|
|
for (i = 1; i < ppe_dev->num_ports; i++) {
|
||
|
|
/* Enable Ethernet port counter */
|
||
|
|
ret = ppe_counter_enable_set(ppe_dev, i);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_MRU_MTU_CTRL_TBL_ADDR + PPE_MRU_MTU_CTRL_TBL_INC * i;
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* Drop the packet when the packet size is more than the MTU
|
||
|
|
* and redirect the packet to the CPU port when the received
|
||
|
|
* packet size is more than the MRU of the physical interface.
|
||
|
|
*/
|
||
|
|
PPE_MRU_MTU_CTRL_SET_MRU_CMD(mru_mtu_val, PPE_ACTION_REDIRECT_TO_CPU);
|
||
|
|
PPE_MRU_MTU_CTRL_SET_MTU_CMD(mru_mtu_val, PPE_ACTION_DROP);
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
mru_mtu_val, ARRAY_SIZE(mru_mtu_val));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
reg = PPE_MC_MTU_CTRL_TBL_ADDR + PPE_MC_MTU_CTRL_TBL_INC * i;
|
||
|
|
val = FIELD_PREP(PPE_MC_MTU_CTRL_TBL_MTU_CMD, PPE_ACTION_DROP);
|
||
|
|
ret = regmap_update_bits(ppe_dev->regmap, reg,
|
||
|
|
PPE_MC_MTU_CTRL_TBL_MTU_CMD,
|
||
|
|
val);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Enable CPU port counters. */
|
||
|
|
return ppe_counter_enable_set(ppe_dev, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Initialize the PPE RSS configuration for IPv4 and IPv6 packet receive.
|
||
|
|
* RSS settings are to calculate the random RSS hash value generated during
|
||
|
|
* packet receive. This hash is then used to generate the queue offset used
|
||
|
|
* to determine the queue used to transmit the packet.
|
||
|
|
*/
|
||
|
|
static int ppe_rss_hash_init(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
u16 fins[PPE_RSS_HASH_TUPLES] = { 0x205, 0x264, 0x227, 0x245, 0x201 };
|
||
|
|
u8 ips[PPE_RSS_HASH_IP_LENGTH] = { 0x13, 0xb, 0x13, 0xb };
|
||
|
|
struct ppe_rss_hash_cfg hash_cfg;
|
||
|
|
int i, ret;
|
||
|
|
|
||
|
|
hash_cfg.hash_seed = get_random_u32();
|
||
|
|
hash_cfg.hash_mask = 0xfff;
|
||
|
|
|
||
|
|
/* Use 5 tuple as RSS hash key for the first fragment of TCP, UDP
|
||
|
|
* and UDP-Lite packets.
|
||
|
|
*/
|
||
|
|
hash_cfg.hash_fragment_mode = false;
|
||
|
|
|
||
|
|
/* The final common seed configs used to calculate the RSS has value,
|
||
|
|
* which is available for both IPv4 and IPv6 packet.
|
||
|
|
*/
|
||
|
|
for (i = 0; i < ARRAY_SIZE(fins); i++) {
|
||
|
|
hash_cfg.hash_fin_inner[i] = fins[i] & 0x1f;
|
||
|
|
hash_cfg.hash_fin_outer[i] = fins[i] >> 5;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* RSS seeds for IP protocol, L4 destination & source port and
|
||
|
|
* destination & source IP used to calculate the RSS hash value.
|
||
|
|
*/
|
||
|
|
hash_cfg.hash_protocol_mix = 0x13;
|
||
|
|
hash_cfg.hash_dport_mix = 0xb;
|
||
|
|
hash_cfg.hash_sport_mix = 0x13;
|
||
|
|
hash_cfg.hash_dip_mix[0] = 0xb;
|
||
|
|
hash_cfg.hash_sip_mix[0] = 0x13;
|
||
|
|
|
||
|
|
/* Configure RSS seed configs for IPv4 packet. */
|
||
|
|
ret = ppe_rss_hash_config_set(ppe_dev, PPE_RSS_HASH_MODE_IPV4, hash_cfg);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
for (i = 0; i < ARRAY_SIZE(ips); i++) {
|
||
|
|
hash_cfg.hash_sip_mix[i] = ips[i];
|
||
|
|
hash_cfg.hash_dip_mix[i] = ips[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Configure RSS seed configs for IPv6 packet. */
|
||
|
|
return ppe_rss_hash_config_set(ppe_dev, PPE_RSS_HASH_MODE_IPV6, hash_cfg);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Initialize mapping between PPE queues assigned to CPU port 0
|
||
|
|
* to Ethernet DMA ring 0.
|
||
|
|
*/
|
||
|
|
static int ppe_queues_to_ring_init(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
u32 queue_bmap[PPE_RING_TO_QUEUE_BITMAP_WORD_CNT] = {};
|
||
|
|
int ret, queue_id, queue_max;
|
||
|
|
|
||
|
|
ret = ppe_port_resource_get(ppe_dev, 0, PPE_RES_UCAST,
|
||
|
|
&queue_id, &queue_max);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
for (; queue_id <= queue_max; queue_id++)
|
||
|
|
queue_bmap[queue_id / 32] |= BIT_MASK(queue_id % 32);
|
||
|
|
|
||
|
|
return ppe_ring_queue_map_set(ppe_dev, 0, queue_bmap);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Initialize PPE bridge settings to only enable L2 frame receive and
|
||
|
|
* transmit between CPU port and PPE Ethernet ports.
|
||
|
|
*/
|
||
|
|
static int ppe_bridge_init(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
u32 reg, mask, port_cfg[4], vsi_cfg[2];
|
||
|
|
int ret, i;
|
||
|
|
|
||
|
|
/* Configure the following settings for CPU port0:
|
||
|
|
* a.) Enable Bridge TX
|
||
|
|
* b.) Disable FDB new address learning
|
||
|
|
* c.) Disable station move address learning
|
||
|
|
*/
|
||
|
|
mask = PPE_PORT_BRIDGE_TXMAC_EN;
|
||
|
|
mask |= PPE_PORT_BRIDGE_NEW_LRN_EN;
|
||
|
|
mask |= PPE_PORT_BRIDGE_STA_MOVE_LRN_EN;
|
||
|
|
ret = regmap_update_bits(ppe_dev->regmap,
|
||
|
|
PPE_PORT_BRIDGE_CTRL_ADDR,
|
||
|
|
mask,
|
||
|
|
PPE_PORT_BRIDGE_TXMAC_EN);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
for (i = 1; i < ppe_dev->num_ports; i++) {
|
||
|
|
/* Enable invalid VSI forwarding for all the physical ports
|
||
|
|
* to CPU port0, in case no VSI is assigned to the physical
|
||
|
|
* port.
|
||
|
|
*/
|
||
|
|
reg = PPE_L2_VP_PORT_TBL_ADDR + PPE_L2_VP_PORT_TBL_INC * i;
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
port_cfg, ARRAY_SIZE(port_cfg));
|
||
|
|
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
PPE_L2_PORT_SET_INVALID_VSI_FWD_EN(port_cfg, true);
|
||
|
|
PPE_L2_PORT_SET_DST_INFO(port_cfg, 0);
|
||
|
|
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
port_cfg, ARRAY_SIZE(port_cfg));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
for (i = 0; i < PPE_VSI_TBL_ENTRIES; i++) {
|
||
|
|
/* Set the VSI forward membership to include only CPU port0.
|
||
|
|
* FDB learning and forwarding take place only after switchdev
|
||
|
|
* is supported later to create the VSI and join the physical
|
||
|
|
* ports to the VSI port member.
|
||
|
|
*/
|
||
|
|
reg = PPE_VSI_TBL_ADDR + PPE_VSI_TBL_INC * i;
|
||
|
|
ret = regmap_bulk_read(ppe_dev->regmap, reg,
|
||
|
|
vsi_cfg, ARRAY_SIZE(vsi_cfg));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
PPE_VSI_SET_MEMBER_PORT_BITMAP(vsi_cfg, BIT(0));
|
||
|
|
PPE_VSI_SET_UUC_BITMAP(vsi_cfg, BIT(0));
|
||
|
|
PPE_VSI_SET_UMC_BITMAP(vsi_cfg, BIT(0));
|
||
|
|
PPE_VSI_SET_BC_BITMAP(vsi_cfg, BIT(0));
|
||
|
|
PPE_VSI_SET_NEW_ADDR_LRN_EN(vsi_cfg, true);
|
||
|
|
PPE_VSI_SET_NEW_ADDR_FWD_CMD(vsi_cfg, PPE_ACTION_FORWARD);
|
||
|
|
PPE_VSI_SET_STATION_MOVE_LRN_EN(vsi_cfg, true);
|
||
|
|
PPE_VSI_SET_STATION_MOVE_FWD_CMD(vsi_cfg, PPE_ACTION_FORWARD);
|
||
|
|
|
||
|
|
ret = regmap_bulk_write(ppe_dev->regmap, reg,
|
||
|
|
vsi_cfg, ARRAY_SIZE(vsi_cfg));
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int ppe_hw_config(struct ppe_device *ppe_dev)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ret = ppe_config_bm(ppe_dev);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = ppe_config_qm(ppe_dev);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = ppe_config_scheduler(ppe_dev);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = ppe_queue_dest_init(ppe_dev);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = ppe_servcode_init(ppe_dev);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = ppe_port_config_init(ppe_dev);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = ppe_rss_hash_init(ppe_dev);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
ret = ppe_queues_to_ring_init(ppe_dev);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
return ppe_bridge_init(ppe_dev);
|
||
|
|
}
|