root/drivers/net/ethernet/qualcomm/ppe/ppe_config.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);
}