mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-08 22:32:55 +00:00
net: lan969x: add function for calculating the DSM calendar
Lan969x has support for RedBox / HSR / PRP (not implemented yet). In order to accommodate for this in the future, we need to give lan969x it's own function for calculating the DSM calendar. The function calculates the calendar for each taxi bus. The calendar is used for bandwidth allocation towards the ports attached to the taxi bus. A calendar configuration consists of up-to 64 slots, which may be allocated to ports or left unused. Each slot accounts for 1 clock cycle. Also expose sparx5_cal_speed_to_value(), sparx5_get_port_cal_speed, sparx5_cal_bw and SPX5_DSM_CAL_EMPTY for use by lan969x. Reviewed-by: Steen Hegelund <Steen.Hegelund@microchip.com> Signed-off-by: Daniel Machon <daniel.machon@microchip.com> Link: https://patch.msgid.link/20241024-sparx5-lan969x-switch-driver-2-v2-11-a0b5fae88a0f@microchip.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
24fe835417
commit
5d2ba39410
@ -5,7 +5,7 @@
|
||||
|
||||
obj-$(CONFIG_LAN969X_SWITCH) += lan969x-switch.o
|
||||
|
||||
lan969x-switch-y := lan969x_regs.o lan969x.o
|
||||
lan969x-switch-y := lan969x_regs.o lan969x.o lan969x_calendar.o
|
||||
|
||||
# Provide include files
|
||||
ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/fdma
|
||||
|
@ -332,6 +332,7 @@ static const struct sparx5_ops lan969x_ops = {
|
||||
.get_sdlb_group = &lan969x_get_sdlb_group,
|
||||
.set_port_mux = &lan969x_port_mux_set,
|
||||
.ptp_irq_handler = &lan969x_ptp_irq_handler,
|
||||
.dsm_calendar_calc = &lan969x_dsm_calendar_calc,
|
||||
};
|
||||
|
||||
const struct sparx5_match_data lan969x_desc = {
|
||||
|
@ -51,4 +51,7 @@ static inline bool lan969x_port_is_25g(int portno)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* lan969x_calendar.c */
|
||||
int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
|
||||
struct sparx5_calendar_data *data);
|
||||
#endif
|
||||
|
191
drivers/net/ethernet/microchip/lan969x/lan969x_calendar.c
Normal file
191
drivers/net/ethernet/microchip/lan969x/lan969x_calendar.c
Normal file
@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* Microchip lan969x Switch driver
|
||||
*
|
||||
* Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
|
||||
*/
|
||||
|
||||
#include "lan969x.h"
|
||||
|
||||
#define LAN969X_DSM_CAL_DEVS_PER_TAXI 10
|
||||
#define LAN969X_DSM_CAL_TAXIS 5
|
||||
|
||||
enum lan969x_dsm_cal_dev {
|
||||
DSM_CAL_DEV_2G5,
|
||||
DSM_CAL_DEV_5G,
|
||||
DSM_CAL_DEV_10G,
|
||||
DSM_CAL_DEV_OTHER, /* 1G or less */
|
||||
DSM_CAL_DEV_MAX
|
||||
};
|
||||
|
||||
/* Each entry in the following struct defines properties for a given speed
|
||||
* (10G, 5G, 2.5G, or 1G or less).
|
||||
*/
|
||||
struct lan969x_dsm_cal_dev_speed {
|
||||
/* Number of devices that requires this speed. */
|
||||
u32 n_devs;
|
||||
|
||||
/* Array of devices that requires this speed. */
|
||||
u32 devs[LAN969X_DSM_CAL_DEVS_PER_TAXI];
|
||||
|
||||
/* Number of slots required for one device running this speed. */
|
||||
u32 n_slots;
|
||||
|
||||
/* Gap between two slots for one device running this speed. */
|
||||
u32 gap;
|
||||
};
|
||||
|
||||
static u32
|
||||
lan969x_taxi_ports[LAN969X_DSM_CAL_TAXIS][LAN969X_DSM_CAL_DEVS_PER_TAXI] = {
|
||||
{ 0, 4, 1, 2, 3, 5, 6, 7, 28, 29 },
|
||||
{ 8, 12, 9, 13, 10, 11, 14, 15, 99, 99 },
|
||||
{ 16, 20, 17, 21, 18, 19, 22, 23, 99, 99 },
|
||||
{ 24, 25, 99, 99, 99, 99, 99, 99, 99, 99 },
|
||||
{ 26, 27, 99, 99, 99, 99, 99, 99, 99, 99 }
|
||||
};
|
||||
|
||||
static int lan969x_dsm_cal_idx_get(u32 *calendar, u32 cal_len, u32 *cal_idx)
|
||||
{
|
||||
if (*cal_idx >= cal_len)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
if (calendar[*cal_idx] == SPX5_DSM_CAL_EMPTY)
|
||||
return 0;
|
||||
|
||||
(*cal_idx)++;
|
||||
} while (*cal_idx < cal_len);
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static enum lan969x_dsm_cal_dev lan969x_dsm_cal_get_dev(int speed)
|
||||
{
|
||||
return (speed == 10000 ? DSM_CAL_DEV_10G :
|
||||
speed == 5000 ? DSM_CAL_DEV_5G :
|
||||
speed == 2500 ? DSM_CAL_DEV_2G5 :
|
||||
DSM_CAL_DEV_OTHER);
|
||||
}
|
||||
|
||||
static int lan969x_dsm_cal_get_speed(enum lan969x_dsm_cal_dev dev)
|
||||
{
|
||||
return (dev == DSM_CAL_DEV_10G ? 10000 :
|
||||
dev == DSM_CAL_DEV_5G ? 5000 :
|
||||
dev == DSM_CAL_DEV_2G5 ? 2500 :
|
||||
1000);
|
||||
}
|
||||
|
||||
int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
|
||||
struct sparx5_calendar_data *data)
|
||||
{
|
||||
struct lan969x_dsm_cal_dev_speed dev_speeds[DSM_CAL_DEV_MAX] = {};
|
||||
u32 cal_len, n_slots, taxi_bw, n_devs = 0, required_bw = 0;
|
||||
struct lan969x_dsm_cal_dev_speed *speed;
|
||||
int err;
|
||||
|
||||
/* Maximum bandwidth for this taxi */
|
||||
taxi_bw = (128 * 1000000) / sparx5_clk_period(sparx5->coreclock);
|
||||
|
||||
memcpy(data->taxi_ports, &lan969x_taxi_ports[taxi],
|
||||
LAN969X_DSM_CAL_DEVS_PER_TAXI * sizeof(u32));
|
||||
|
||||
for (int i = 0; i < LAN969X_DSM_CAL_DEVS_PER_TAXI; i++) {
|
||||
u32 portno = data->taxi_ports[i];
|
||||
enum sparx5_cal_bw bw;
|
||||
|
||||
bw = sparx5_get_port_cal_speed(sparx5, portno);
|
||||
|
||||
if (portno < sparx5->data->consts->n_ports_all)
|
||||
data->taxi_speeds[i] = sparx5_cal_speed_to_value(bw);
|
||||
else
|
||||
data->taxi_speeds[i] = 0;
|
||||
}
|
||||
|
||||
/* Determine the different port types (10G, 5G, 2.5G, <= 1G) in the
|
||||
* this taxi map.
|
||||
*/
|
||||
for (int i = 0; i < LAN969X_DSM_CAL_DEVS_PER_TAXI; i++) {
|
||||
u32 taxi_speed = data->taxi_speeds[i];
|
||||
enum lan969x_dsm_cal_dev dev;
|
||||
|
||||
if (taxi_speed == 0)
|
||||
continue;
|
||||
|
||||
required_bw += taxi_speed;
|
||||
|
||||
dev = lan969x_dsm_cal_get_dev(taxi_speed);
|
||||
speed = &dev_speeds[dev];
|
||||
speed->devs[speed->n_devs++] = i;
|
||||
n_devs++;
|
||||
}
|
||||
|
||||
if (required_bw > taxi_bw) {
|
||||
pr_err("Required bandwidth: %u is higher than total taxi bandwidth: %u",
|
||||
required_bw, taxi_bw);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (n_devs == 0) {
|
||||
data->schedule[0] = SPX5_DSM_CAL_EMPTY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cal_len = n_devs;
|
||||
|
||||
/* Search for a calendar length that fits all active devices. */
|
||||
while (cal_len < SPX5_DSM_CAL_LEN) {
|
||||
u32 bw_per_slot = taxi_bw / cal_len;
|
||||
|
||||
n_slots = 0;
|
||||
|
||||
for (int i = 0; i < DSM_CAL_DEV_MAX; i++) {
|
||||
speed = &dev_speeds[i];
|
||||
|
||||
if (speed->n_devs == 0)
|
||||
continue;
|
||||
|
||||
required_bw = lan969x_dsm_cal_get_speed(i);
|
||||
speed->n_slots = DIV_ROUND_UP(required_bw, bw_per_slot);
|
||||
|
||||
if (speed->n_slots)
|
||||
speed->gap = DIV_ROUND_UP(cal_len,
|
||||
speed->n_slots);
|
||||
else
|
||||
speed->gap = 0;
|
||||
|
||||
n_slots += speed->n_slots * speed->n_devs;
|
||||
}
|
||||
|
||||
if (n_slots <= cal_len)
|
||||
break; /* Found a suitable calendar length. */
|
||||
|
||||
/* Not good enough yet. */
|
||||
cal_len = n_slots;
|
||||
}
|
||||
|
||||
if (cal_len > SPX5_DSM_CAL_LEN) {
|
||||
pr_err("Invalid length: %u for taxi: %u", cal_len, taxi);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < SPX5_DSM_CAL_LEN; i++)
|
||||
data->schedule[i] = SPX5_DSM_CAL_EMPTY;
|
||||
|
||||
/* Place the remaining devices */
|
||||
for (u32 i = 0; i < DSM_CAL_DEV_MAX; i++) {
|
||||
speed = &dev_speeds[i];
|
||||
for (u32 dev = 0; dev < speed->n_devs; dev++) {
|
||||
u32 idx = 0;
|
||||
|
||||
for (n_slots = 0; n_slots < speed->n_slots; n_slots++) {
|
||||
err = lan969x_dsm_cal_idx_get(data->schedule,
|
||||
cal_len, &idx);
|
||||
if (err)
|
||||
return err;
|
||||
data->schedule[idx] = speed->devs[dev];
|
||||
idx += speed->gap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -15,7 +15,6 @@
|
||||
#define SPX5_CALBITS_PER_PORT 3 /* Bit per port in calendar register */
|
||||
|
||||
/* DSM calendar information */
|
||||
#define SPX5_DSM_CAL_EMPTY 0xFFFF
|
||||
#define SPX5_DSM_CAL_TAXIS 8
|
||||
#define SPX5_DSM_CAL_BW_LOSS 553
|
||||
|
||||
@ -74,18 +73,6 @@ static u32 sparx5_target_bandwidth(struct sparx5 *sparx5)
|
||||
}
|
||||
}
|
||||
|
||||
/* This is used in calendar configuration */
|
||||
enum sparx5_cal_bw {
|
||||
SPX5_CAL_SPEED_NONE = 0,
|
||||
SPX5_CAL_SPEED_1G = 1,
|
||||
SPX5_CAL_SPEED_2G5 = 2,
|
||||
SPX5_CAL_SPEED_5G = 3,
|
||||
SPX5_CAL_SPEED_10G = 4,
|
||||
SPX5_CAL_SPEED_25G = 5,
|
||||
SPX5_CAL_SPEED_0G5 = 6,
|
||||
SPX5_CAL_SPEED_12G5 = 7
|
||||
};
|
||||
|
||||
static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
|
||||
{
|
||||
switch (cclock) {
|
||||
@ -98,7 +85,7 @@ static u32 sparx5_clk_to_bandwidth(enum sparx5_core_clockfreq cclock)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
|
||||
u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
|
||||
{
|
||||
switch (speed) {
|
||||
case SPX5_CAL_SPEED_1G: return 1000;
|
||||
@ -111,6 +98,7 @@ static u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed)
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sparx5_cal_speed_to_value);
|
||||
|
||||
static u32 sparx5_bandwidth_to_calendar(u32 bw)
|
||||
{
|
||||
@ -128,8 +116,7 @@ static u32 sparx5_bandwidth_to_calendar(u32 bw)
|
||||
}
|
||||
}
|
||||
|
||||
static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
|
||||
u32 portno)
|
||||
enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, u32 portno)
|
||||
{
|
||||
struct sparx5_port *port;
|
||||
|
||||
@ -163,6 +150,7 @@ static enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5,
|
||||
return SPX5_CAL_SPEED_NONE;
|
||||
return sparx5_bandwidth_to_calendar(port->conf.bandwidth);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sparx5_get_port_cal_speed);
|
||||
|
||||
/* Auto configure the QSYS calendar based on port configuration */
|
||||
int sparx5_config_auto_calendar(struct sparx5 *sparx5)
|
||||
|
@ -63,6 +63,18 @@ enum sparx5_vlan_port_type {
|
||||
SPX5_VLAN_PORT_TYPE_S_CUSTOM /* S-port using custom type */
|
||||
};
|
||||
|
||||
/* This is used in calendar configuration */
|
||||
enum sparx5_cal_bw {
|
||||
SPX5_CAL_SPEED_NONE = 0,
|
||||
SPX5_CAL_SPEED_1G = 1,
|
||||
SPX5_CAL_SPEED_2G5 = 2,
|
||||
SPX5_CAL_SPEED_5G = 3,
|
||||
SPX5_CAL_SPEED_10G = 4,
|
||||
SPX5_CAL_SPEED_25G = 5,
|
||||
SPX5_CAL_SPEED_0G5 = 6,
|
||||
SPX5_CAL_SPEED_12G5 = 7
|
||||
};
|
||||
|
||||
#define SPX5_PORTS 65
|
||||
#define SPX5_PORTS_ALL 70 /* Total number of ports */
|
||||
|
||||
@ -113,6 +125,7 @@ enum sparx5_vlan_port_type {
|
||||
|
||||
#define SPX5_DSM_CAL_LEN 64
|
||||
#define SPX5_DSM_CAL_MAX_DEVS_PER_TAXI 13
|
||||
#define SPX5_DSM_CAL_EMPTY 0xFFFF
|
||||
|
||||
#define SPARX5_MAX_PTP_ID 512
|
||||
|
||||
@ -454,6 +467,8 @@ int sparx5_config_auto_calendar(struct sparx5 *sparx5);
|
||||
int sparx5_config_dsm_calendar(struct sparx5 *sparx5);
|
||||
int sparx5_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
|
||||
struct sparx5_calendar_data *data);
|
||||
u32 sparx5_cal_speed_to_value(enum sparx5_cal_bw speed);
|
||||
enum sparx5_cal_bw sparx5_get_port_cal_speed(struct sparx5 *sparx5, u32 portno);
|
||||
|
||||
|
||||
/* sparx5_ethtool.c */
|
||||
|
Loading…
Reference in New Issue
Block a user