xref: /linux/drivers/net/ethernet/microchip/sparx5/lan969x/lan969x_calendar.c (revision 150b567e0d572342ef08bace7ee7aff80fd75327)
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 
12 enum 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  */
23 struct 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 
37 static u32
38 lan969x_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 
lan969x_dsm_cal_idx_get(u32 * calendar,u32 cal_len,u32 * cal_idx)46 static 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 
lan969x_dsm_cal_get_dev(int speed)61 static 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 
lan969x_dsm_cal_get_speed(enum lan969x_dsm_cal_dev dev)69 static 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 
lan969x_dsm_calendar_calc(struct sparx5 * sparx5,u32 taxi,struct sparx5_calendar_data * data)77 int 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 }
192