File Index
Files and directories
Sign in
FILE

lan969x_calendar.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/* Microchip lan969x Switch driver
  3 *
  4 * Copyright (c) 2024 Microchip Technology Inc. and its subsidiaries.
  5 */
  6
  7#include "lan969x.h"
  8
  9#define LAN969X_DSM_CAL_DEVS_PER_TAXI 10
 10#define LAN969X_DSM_CAL_TAXIS 5
 11
 12enum lan969x_dsm_cal_dev {
 13	DSM_CAL_DEV_2G5,
 14	DSM_CAL_DEV_5G,
 15	DSM_CAL_DEV_10G,
 16	DSM_CAL_DEV_OTHER, /* 1G or less */
 17	DSM_CAL_DEV_MAX
 18};
 19
 20/* Each entry in the following struct defines properties for a given speed
 21 * (10G, 5G, 2.5G, or 1G or less).
 22 */
 23struct lan969x_dsm_cal_dev_speed {
 24	/* Number of devices that requires this speed. */
 25	u32 n_devs;
 26
 27	/* Array of devices that requires this speed. */
 28	u32 devs[LAN969X_DSM_CAL_DEVS_PER_TAXI];
 29
 30	/* Number of slots required for one device running this speed. */
 31	u32 n_slots;
 32
 33	/* Gap between two slots for one device running this speed. */
 34	u32 gap;
 35};
 36
 37static u32
 38lan969x_taxi_ports[LAN969X_DSM_CAL_TAXIS][LAN969X_DSM_CAL_DEVS_PER_TAXI] = {
 39	{  0,  4,  1,  2,  3,  5,  6,  7, 28, 29 },
 40	{  8, 12,  9, 13, 10, 11, 14, 15, 99, 99 },
 41	{ 16, 20, 17, 21, 18, 19, 22, 23, 99, 99 },
 42	{ 24, 25, 99, 99, 99, 99, 99, 99, 99, 99 },
 43	{ 26, 27, 99, 99, 99, 99, 99, 99, 99, 99 }
 44};
 45
 46static int lan969x_dsm_cal_idx_get(u32 *calendar, u32 cal_len, u32 *cal_idx)
 47{
 48	if (*cal_idx >= cal_len)
 49		return -EINVAL;
 50
 51	do {
 52		if (calendar[*cal_idx] == SPX5_DSM_CAL_EMPTY)
 53			return 0;
 54
 55		(*cal_idx)++;
 56	} while (*cal_idx < cal_len);
 57
 58	return -ENOENT;
 59}
 60
 61static enum lan969x_dsm_cal_dev lan969x_dsm_cal_get_dev(int speed)
 62{
 63	return (speed == 10000 ? DSM_CAL_DEV_10G :
 64		speed == 5000  ? DSM_CAL_DEV_5G :
 65		speed == 2500  ? DSM_CAL_DEV_2G5 :
 66				 DSM_CAL_DEV_OTHER);
 67}
 68
 69static int lan969x_dsm_cal_get_speed(enum lan969x_dsm_cal_dev dev)
 70{
 71	return (dev == DSM_CAL_DEV_10G ? 10000 :
 72		dev == DSM_CAL_DEV_5G  ? 5000 :
 73		dev == DSM_CAL_DEV_2G5 ? 2500 :
 74					 1000);
 75}
 76
 77int lan969x_dsm_calendar_calc(struct sparx5 *sparx5, u32 taxi,
 78			      struct sparx5_calendar_data *data)
 79{
 80	struct lan969x_dsm_cal_dev_speed dev_speeds[DSM_CAL_DEV_MAX] = {};
 81	u32 cal_len, n_slots, taxi_bw, n_devs = 0, required_bw  = 0;
 82	struct lan969x_dsm_cal_dev_speed *speed;
 83	int err;
 84
 85	/* Maximum bandwidth for this taxi */
 86	taxi_bw = (128 * 1000000) / sparx5_clk_period(sparx5->coreclock);
 87
 88	memcpy(data->taxi_ports, &lan969x_taxi_ports[taxi],
 89	       LAN969X_DSM_CAL_DEVS_PER_TAXI * sizeof(u32));
 90
 91	for (int i = 0; i < LAN969X_DSM_CAL_DEVS_PER_TAXI; i++) {
 92		u32 portno = data->taxi_ports[i];
 93		enum sparx5_cal_bw bw;
 94
 95		bw = sparx5_get_port_cal_speed(sparx5, portno);
 96
 97		if (portno < sparx5->data->consts->n_ports_all)
 98			data->taxi_speeds[i] = sparx5_cal_speed_to_value(bw);
 99		else
100			data->taxi_speeds[i] = 0;
101	}
102
103	/* Determine the different port types (10G, 5G, 2.5G, <= 1G) in the
104	 * this taxi map.
105	 */
106	for (int i = 0; i < LAN969X_DSM_CAL_DEVS_PER_TAXI; i++) {
107		u32 taxi_speed = data->taxi_speeds[i];
108		enum lan969x_dsm_cal_dev dev;
109
110		if (taxi_speed == 0)
111			continue;
112
113		required_bw += taxi_speed;
114
115		dev = lan969x_dsm_cal_get_dev(taxi_speed);
116		speed = &dev_speeds[dev];
117		speed->devs[speed->n_devs++] = i;
118		n_devs++;
119	}
120
121	if (required_bw > taxi_bw) {
122		pr_err("Required bandwidth: %u is higher than total taxi bandwidth: %u",
123		       required_bw, taxi_bw);
124		return -EINVAL;
125	}
126
127	if (n_devs == 0) {
128		data->schedule[0] = SPX5_DSM_CAL_EMPTY;
129		return 0;
130	}
131
132	cal_len = n_devs;
133
134	/* Search for a calendar length that fits all active devices. */
135	while (cal_len < SPX5_DSM_CAL_LEN) {
136		u32 bw_per_slot = taxi_bw / cal_len;
137
138		n_slots = 0;
139
140		for (int i = 0; i < DSM_CAL_DEV_MAX; i++) {
141			speed = &dev_speeds[i];
142
143			if (speed->n_devs == 0)
144				continue;
145
146			required_bw = lan969x_dsm_cal_get_speed(i);
147			speed->n_slots = DIV_ROUND_UP(required_bw, bw_per_slot);
148
149			if (speed->n_slots)
150				speed->gap = DIV_ROUND_UP(cal_len,
151							  speed->n_slots);
152			else
153				speed->gap = 0;
154
155			n_slots += speed->n_slots * speed->n_devs;
156		}
157
158		if (n_slots <= cal_len)
159			break; /* Found a suitable calendar length. */
160
161		/* Not good enough yet. */
162		cal_len = n_slots;
163	}
164
165	if (cal_len > SPX5_DSM_CAL_LEN) {
166		pr_err("Invalid length: %u for taxi: %u", cal_len, taxi);
167		return -EINVAL;
168	}
169
170	for (u32 i = 0; i < SPX5_DSM_CAL_LEN; i++)
171		data->schedule[i] = SPX5_DSM_CAL_EMPTY;
172
173	/* Place the remaining devices */
174	for (u32 i = 0; i < DSM_CAL_DEV_MAX; i++) {
175		speed = &dev_speeds[i];
176		for (u32 dev = 0; dev < speed->n_devs; dev++) {
177			u32 idx = 0;
178
179			for (n_slots = 0; n_slots < speed->n_slots; n_slots++) {
180				err = lan969x_dsm_cal_idx_get(data->schedule,
181							      cal_len, &idx);
182				if (err)
183					return err;
184				data->schedule[idx] = speed->devs[dev];
185				idx += speed->gap;
186			}
187		}
188	}
189
190	return 0;
191}
~/ Read-only file preview.