// 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 #include #include #include #include #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); }