summaryrefslogtreecommitdiff
path: root/drivers/net/ti/icssg_config.c
blob: 43ce6e15da386eb2e31204bf39015985d5b7e843 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
// SPDX-License-Identifier: GPL-2.0
/* ICSSG Ethernet driver
 *
 * Copyright (C) 2018-2024 Texas Instruments Incorporated - http://www.ti.com
 */

#include <phy.h>
#include "icssg_prueth.h"
#include "icssg_switch_map.h"
#include "icss_mii_rt.h"
#include <dm/device_compat.h>
#include <linux/iopoll.h>

/* TX IPG Values to be set for 100M and 1G link speeds.  These values are
 * in ocp_clk cycles. So need change if ocp_clk is changed for a specific
 * h/w design.
 */

/* SR2.0 IPG is in rgmii_clk (125MHz) clock cycles + 1 */
#define MII_RT_TX_IPG_100M      0x17
#define MII_RT_TX_IPG_1G        0xb

#define	ICSSG_QUEUES_MAX		64
#define	ICSSG_QUEUE_OFFSET		0xd00
#define	ICSSG_QUEUE_PEEK_OFFSET		0xe00
#define	ICSSG_QUEUE_CNT_OFFSET		0xe40
#define	ICSSG_QUEUE_RESET_OFFSET	0xf40

#define	ICSSG_NUM_TX_QUEUES	8

#define	RECYCLE_Q_SLICE0	16
#define	RECYCLE_Q_SLICE1	17

#define	ICSSG_NUM_OTHER_QUEUES	5	/* port, host and special queues */

#define	PORT_HI_Q_SLICE0	32
#define	PORT_LO_Q_SLICE0	33
#define	HOST_HI_Q_SLICE0	34
#define	HOST_LO_Q_SLICE0	35
#define	HOST_SPL_Q_SLICE0	40	/* Special Queue */

#define	PORT_HI_Q_SLICE1	36
#define	PORT_LO_Q_SLICE1	37
#define	HOST_HI_Q_SLICE1	38
#define	HOST_LO_Q_SLICE1	39
#define	HOST_SPL_Q_SLICE1	41	/* Special Queue */

#define MII_RXCFG_DEFAULT	(PRUSS_MII_RT_RXCFG_RX_ENABLE | \
				 PRUSS_MII_RT_RXCFG_RX_DATA_RDY_MODE_DIS | \
				 PRUSS_MII_RT_RXCFG_RX_L2_EN | \
				 PRUSS_MII_RT_RXCFG_RX_L2_EOF_SCLR_DIS)

#define MII_TXCFG_DEFAULT	(PRUSS_MII_RT_TXCFG_TX_ENABLE | \
				 PRUSS_MII_RT_TXCFG_TX_AUTO_PREAMBLE | \
				 PRUSS_MII_RT_TXCFG_TX_32_MODE_EN | \
				 PRUSS_MII_RT_TXCFG_TX_IPG_WIRE_CLK_EN)

#define ICSSG_CFG_DEFAULT	(ICSSG_CFG_TX_L1_EN | \
				 ICSSG_CFG_TX_L2_EN | ICSSG_CFG_RX_L2_G_EN | \
				 ICSSG_CFG_TX_PRU_EN | /* SR2.0 only */ \
				 ICSSG_CFG_SGMII_MODE)

#define FDB_GEN_CFG1		0x60
#define SMEM_VLAN_OFFSET	8
#define SMEM_VLAN_OFFSET_MASK	GENMASK(25, 8)

#define FDB_GEN_CFG2		0x64
#define FDB_VLAN_EN		BIT(6)
#define FDB_HOST_EN		BIT(2)
#define FDB_PRU1_EN		BIT(1)
#define FDB_PRU0_EN		BIT(0)
#define FDB_EN_ALL		(FDB_PRU0_EN | FDB_PRU1_EN | \
				 FDB_HOST_EN | FDB_VLAN_EN)

struct map {
	int queue;
	u32 pd_addr_start;
	u32 flags;
	bool special;
};

struct map hwq_map[2][ICSSG_NUM_OTHER_QUEUES] = {
	{
		{ PORT_HI_Q_SLICE0, PORT_DESC0_HI, 0x200000, 0 },
		{ PORT_LO_Q_SLICE0, PORT_DESC0_LO, 0, 0 },
		{ HOST_HI_Q_SLICE0, HOST_DESC0_HI, 0x200000, 0 },
		{ HOST_LO_Q_SLICE0, HOST_DESC0_LO, 0, 0 },
		{ HOST_SPL_Q_SLICE0, HOST_SPPD0, 0x400000, 1 },
	},
	{
		{ PORT_HI_Q_SLICE1, PORT_DESC1_HI, 0xa00000, 0 },
		{ PORT_LO_Q_SLICE1, PORT_DESC1_LO, 0x800000, 0 },
		{ HOST_HI_Q_SLICE1, HOST_DESC1_HI, 0xa00000, 0 },
		{ HOST_LO_Q_SLICE1, HOST_DESC1_LO, 0x800000, 0 },
		{ HOST_SPL_Q_SLICE1, HOST_SPPD1, 0xc00000, 1 },
	},
};

static void icssg_config_mii_init(struct prueth *prueth, int slice)
{
	struct regmap *mii_rt = prueth->mii_rt;
	u32 txcfg_reg, pcnt_reg;
	u32 txcfg;

	txcfg_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_TXCFG0 :
				       PRUSS_MII_RT_TXCFG1;
	pcnt_reg = (slice == ICSS_MII0) ? PRUSS_MII_RT_RX_PCNT0 :
				       PRUSS_MII_RT_RX_PCNT1;

	txcfg = MII_TXCFG_DEFAULT;

	if (prueth->phy_interface == PHY_INTERFACE_MODE_MII && slice == ICSS_MII0)
		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;
	else if (prueth->phy_interface != PHY_INTERFACE_MODE_MII && slice == ICSS_MII1)
		txcfg |= PRUSS_MII_RT_TXCFG_TX_MUX_SEL;

	regmap_write(mii_rt, txcfg_reg, txcfg);
	regmap_write(mii_rt, pcnt_reg, 0x1);
}

static void icssg_miig_queues_init(struct prueth *prueth, int slice)
{
	void __iomem *smem = (void __iomem *)prueth->shram.pa;
	struct regmap *miig_rt = prueth->miig_rt;
	int queue = 0, i, j;
	u8 pd[ICSSG_SPECIAL_PD_SIZE];
	u32 *pdword;

	/* reset hwqueues */
	if (slice)
		queue = ICSSG_NUM_TX_QUEUES;

	for (i = 0; i < ICSSG_NUM_TX_QUEUES; i++) {
		regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue);
		queue++;
	}

	queue = slice ? RECYCLE_Q_SLICE1 : RECYCLE_Q_SLICE0;
	regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET, queue);

	for (i = 0; i < ICSSG_NUM_OTHER_QUEUES; i++) {
		regmap_write(miig_rt, ICSSG_QUEUE_RESET_OFFSET,
			     hwq_map[slice][i].queue);
	}

	/* initialize packet descriptors in SMEM */
	/* push pakcet descriptors to hwqueues */

	pdword = (u32 *)pd;
	for (j = 0; j < ICSSG_NUM_OTHER_QUEUES; j++) {
		struct map *mp;
		int pd_size, num_pds;
		u32 pdaddr;

		mp = &hwq_map[slice][j];
		if (mp->special) {
			pd_size = ICSSG_SPECIAL_PD_SIZE;
			num_pds = ICSSG_NUM_SPECIAL_PDS;
		} else	{
			pd_size = ICSSG_NORMAL_PD_SIZE;
			num_pds = ICSSG_NUM_NORMAL_PDS;
		}

		for (i = 0; i < num_pds; i++) {
			memset(pd, 0, pd_size);

			pdword[0] &= cpu_to_le32(ICSSG_FLAG_MASK);
			pdword[0] |= cpu_to_le32(mp->flags);
			pdaddr = mp->pd_addr_start + i * pd_size;

			memcpy_toio(smem + pdaddr, pd, pd_size);
			queue = mp->queue;
			regmap_write(miig_rt, ICSSG_QUEUE_OFFSET + 4 * queue,
				     pdaddr);
		}
	}
}

void icssg_config_ipg(struct prueth *prueth, int speed, int mii)
{
	switch (speed) {
	case SPEED_1000:
		icssg_mii_update_ipg(prueth->mii_rt, mii, MII_RT_TX_IPG_1G);
		break;
	case SPEED_100:
		icssg_mii_update_ipg(prueth->mii_rt, mii, MII_RT_TX_IPG_100M);
		break;
	default:
		/* Other links speeds not supported */
		pr_err("Unsupported link speed\n");
		return;
	}
}

static void emac_r30_cmd_init(struct prueth *prueth)
{
	int i;
	struct icssg_r30_cmd *p;

	p = (struct icssg_r30_cmd *)(prueth->dram.pa + MGR_R30_CMD_OFFSET);

	for (i = 0; i < 4; i++)
		writel(EMAC_NONE, &p->cmd[i]);
}

static int emac_r30_is_done(struct prueth *prueth)
{
	const struct icssg_r30_cmd *p;
	int i;
	u32 cmd;

	p = (const struct icssg_r30_cmd *)(prueth->dram.pa + MGR_R30_CMD_OFFSET);

	for (i = 0; i < 4; i++) {
		cmd = readl(&p->cmd[i]);
		if (cmd != EMAC_NONE)
			return 0;
	}

	return 1;
}

static int prueth_emac_buffer_setup(struct prueth *prueth)
{
	struct icssg_buffer_pool_cfg *bpool_cfg;
	struct icssg_rxq_ctx *rxq_ctx;
	int slice = prueth->slice;
	u32 addr;
	int i;

	/* Layout to have 64KB aligned buffer pool
	 * |BPOOL0|BPOOL1|RX_CTX0|RX_CTX1|
	 */

	addr = lower_32_bits(prueth->sram_pa);
	if (slice)
		addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE;

	if (addr % SZ_64K) {
		dev_warn(prueth->dev, "buffer pool needs to be 64KB aligned\n");
		return -EINVAL;
	}

	bpool_cfg = (struct icssg_buffer_pool_cfg *)(prueth->dram.pa + BUFFER_POOL_0_ADDR_OFFSET);
	/* workaround for f/w bug. bpool 0 needs to be initilalized */
	bpool_cfg[0].addr = cpu_to_le32(addr);
	bpool_cfg[0].len = 0;

	for (i = PRUETH_EMAC_BUF_POOL_START;
	     i < (PRUETH_EMAC_BUF_POOL_START + PRUETH_NUM_BUF_POOLS);
	     i++) {
		bpool_cfg[i].addr = cpu_to_le32(addr);
		bpool_cfg[i].len = cpu_to_le32(PRUETH_EMAC_BUF_POOL_SIZE);
		addr += PRUETH_EMAC_BUF_POOL_SIZE;
	}

	if (!slice)
		addr += PRUETH_NUM_BUF_POOLS * PRUETH_EMAC_BUF_POOL_SIZE;
	else
		addr += PRUETH_EMAC_RX_CTX_BUF_SIZE * 2;

	rxq_ctx = (struct icssg_rxq_ctx *)(prueth->dram.pa + HOST_RX_Q_PRE_CONTEXT_OFFSET);

	for (i = 0; i < 3; i++)
		rxq_ctx->start[i] = cpu_to_le32(addr);

	addr += PRUETH_EMAC_RX_CTX_BUF_SIZE;
	rxq_ctx->end = cpu_to_le32(addr);

	/* Express RX buffer queue */
	rxq_ctx = (struct icssg_rxq_ctx *)(prueth->dram.pa + HOST_RX_Q_EXP_CONTEXT_OFFSET);
	for (i = 0; i < 3; i++)
		rxq_ctx->start[i] = cpu_to_le32(addr);

	addr += PRUETH_EMAC_RX_CTX_BUF_SIZE;
	rxq_ctx->end = cpu_to_le32(addr);

	return 0;
}

static void icssg_init_emac_mode(struct prueth *prueth)
{
	u8 mac[6] = { 0 };

	regmap_update_bits(prueth->miig_rt, FDB_GEN_CFG1, SMEM_VLAN_OFFSET_MASK, 0);
	regmap_write(prueth->miig_rt, FDB_GEN_CFG2, 0);
	/* Clear host MAC address */
	icssg_class_set_host_mac_addr(prueth->miig_rt, mac);
}

int icssg_config(struct prueth *prueth)
{
	void *config = (void *)(prueth->dram.pa + ICSSG_CONFIG_OFFSET);
	u8 *cfg_byte_ptr = config;
	struct icssg_flow_cfg *flow_cfg;
	u32 mask;
	int ret;

	int slice = prueth->slice;

	icssg_init_emac_mode(prueth);

	memset_io(config, 0, TAS_GATE_MASK_LIST0);
	icssg_miig_queues_init(prueth, slice);

	prueth->speed = SPEED_1000;
	prueth->duplex = DUPLEX_FULL;
	if (!phy_interface_is_rgmii(prueth->phydev)) {
		prueth->speed = SPEED_100;
		prueth->duplex = DUPLEX_FULL;
	}

	regmap_update_bits(prueth->miig_rt, ICSSG_CFG_OFFSET,
			   ICSSG_CFG_DEFAULT, ICSSG_CFG_DEFAULT);
	icssg_miig_set_interface_mode(prueth->miig_rt, ICSS_MII0, prueth->phy_interface);
	icssg_miig_set_interface_mode(prueth->miig_rt, ICSS_MII1, prueth->phy_interface);
	icssg_config_mii_init(prueth, slice);

	icssg_config_ipg(prueth, SPEED_1000, slice);
	icssg_update_rgmii_cfg(prueth->miig_rt, SPEED_1000, true, slice, prueth);

	/* set GPI mode */
	pruss_cfg_gpimode(prueth->pruss, slice, PRUSS_GPI_MODE_MII);

	/* enable XFR shift for PRU and RTU */
	mask = PRUSS_SPP_XFER_SHIFT_EN | PRUSS_SPP_RTU_XFR_SHIFT_EN;
	pruss_cfg_update(prueth->pruss, PRUSS_CFG_SPP, mask, mask);

	flow_cfg = config + PSI_L_REGULAR_FLOW_ID_BASE_OFFSET;
	flow_cfg->rx_base_flow = prueth->dma_rx.id;
	flow_cfg->mgm_base_flow = 0;
	*(cfg_byte_ptr + SPL_PKT_DEFAULT_PRIORITY) = 0;
	*(cfg_byte_ptr + QUEUE_NUM_UNTAGGED) = 0x0;

	ret = prueth_emac_buffer_setup(prueth);

	if (ret)
		return ret;

	emac_r30_cmd_init(prueth);
	return 0;
}

/* commands to program ICSSG R30 registers */
static struct icssg_r30_cmd emac_r32_bitmask[] = {
	{{0xffff0004, 0xffff0100, 0xffff0004, EMAC_NONE}},	/* EMAC_PORT_DISABLE */
	{{0xfffb0040, 0xfeff0200, 0xfeff0200, EMAC_NONE}},	/* EMAC_PORT_BLOCK */
	{{0xffbb0000, 0xfcff0000, 0xdcfb0000, EMAC_NONE}},	/* EMAC_PORT_FORWARD */
	{{0xffbb0000, 0xfcff0000, 0xfcff2000, EMAC_NONE}},	/* EMAC_PORT_FORWARD_WO_LEARNING */
	{{0xffff0001, EMAC_NONE,  EMAC_NONE, EMAC_NONE}},	/* ACCEPT ALL */
	{{0xfffe0002, EMAC_NONE,  EMAC_NONE, EMAC_NONE}},	/* ACCEPT TAGGED */
	{{0xfffc0000, EMAC_NONE,  EMAC_NONE, EMAC_NONE}},	/* ACCEPT UNTAGGED and PRIO */
	{{EMAC_NONE,  0xffff0020, EMAC_NONE, EMAC_NONE}},	/* TAS Trigger List change */
	{{EMAC_NONE,  0xdfff1000, EMAC_NONE, EMAC_NONE}},	/* TAS set state ENABLE*/
	{{EMAC_NONE,  0xefff2000, EMAC_NONE, EMAC_NONE}},	/* TAS set state RESET*/
	{{EMAC_NONE,  0xcfff0000, EMAC_NONE, EMAC_NONE}},	/* TAS set state DISABLE*/
	{{EMAC_NONE,  EMAC_NONE,  0xffff0400, EMAC_NONE}},	/* UC flooding ENABLE*/
	{{EMAC_NONE,  EMAC_NONE,  0xfbff0000, EMAC_NONE}},	/* UC flooding DISABLE*/
	{{EMAC_NONE,  EMAC_NONE,  0xffff0800, EMAC_NONE}},	/* MC flooding ENABLE*/
	{{EMAC_NONE,  EMAC_NONE,  0xf7ff0000, EMAC_NONE}},	/* MC flooding DISABLE*/
	{{EMAC_NONE,  0xffff4000, EMAC_NONE, EMAC_NONE}},	/* Preemption on Tx ENABLE*/
	{{EMAC_NONE,  0xbfff0000, EMAC_NONE, EMAC_NONE}}	/* Preemption on Tx DISABLE*/
};

int emac_set_port_state(struct prueth *prueth,
			enum icssg_port_state_cmd cmd)
{
	struct icssg_r30_cmd *p;
	int ret = -ETIMEDOUT;
	int timeout = 10;
	int i;

	p = (struct icssg_r30_cmd *)(prueth->dram.pa + MGR_R30_CMD_OFFSET);

	if (cmd >= ICSSG_EMAC_PORT_MAX_COMMANDS) {
		dev_err(prueth->dev, "invalid port command\n");
		return -EINVAL;
	}

	for (i = 0; i < 4; i++)
		writel(emac_r32_bitmask[cmd].cmd[i], &p->cmd[i]);

	/* wait for done */
	while (timeout) {
		if (emac_r30_is_done(prueth)) {
			ret = 0;
			break;
		}

		udelay(2000);
		timeout--;
	}

	if (ret == -ETIMEDOUT)
		dev_err(prueth->dev, "timeout waiting for command done\n");

	return ret;
}

int icssg_send_fdb_msg(struct prueth *prueth, struct mgmt_cmd *cmd,
		       struct mgmt_cmd_rsp *rsp)
{
	int slice = prueth->slice;
	int ret, addr;

	addr = icssg_queue_pop(prueth, slice == 0 ?
			       ICSSG_CMD_POP_SLICE0 : ICSSG_CMD_POP_SLICE1);
	if (addr < 0)
		return addr;

	/* First 4 bytes have FW owned buffer linking info which should
	 * not be touched
	 */
	memcpy_toio((void __iomem *)prueth->shram.pa + addr + 4, cmd, sizeof(*cmd));
	icssg_queue_push(prueth, slice == 0 ?
			 ICSSG_CMD_PUSH_SLICE0 : ICSSG_CMD_PUSH_SLICE1, addr);
	ret = read_poll_timeout(icssg_queue_pop, addr, addr >= 0,
				2000, 20000000, prueth, slice == 0 ?
				ICSSG_RSP_POP_SLICE0 : ICSSG_RSP_POP_SLICE1);

	if (ret) {
		dev_err(prueth->dev, "Timedout sending HWQ message\n");
		return ret;
	}

	memcpy_fromio(rsp, (void __iomem *)prueth->shram.pa + addr, sizeof(*rsp));
	/* Return buffer back for to pool */
	icssg_queue_push(prueth, slice == 0 ?
			 ICSSG_RSP_PUSH_SLICE0 : ICSSG_RSP_PUSH_SLICE1, addr);

	return 0;
}

int emac_fdb_flow_id_updated(struct prueth *prueth)
{
	struct mgmt_cmd_rsp fdb_cmd_rsp = { 0 };
	int slice = prueth->slice;
	struct mgmt_cmd fdb_cmd = { 0 };
	int ret = 0;

	fdb_cmd.header = ICSSG_FW_MGMT_CMD_HEADER;
	fdb_cmd.type   = ICSSG_FW_MGMT_FDB_CMD_TYPE_RX_FLOW;
	fdb_cmd.seqnum = ++(prueth->icssg_hwcmdseq);
	fdb_cmd.param  = 0;

	fdb_cmd.param |= (slice << 4);
	fdb_cmd.cmd_args[0] = 0;

	ret = icssg_send_fdb_msg(prueth, &fdb_cmd, &fdb_cmd_rsp);
	if (ret)
		return ret;

	if (fdb_cmd.seqnum != fdb_cmd_rsp.seqnum) {
		dev_err(prueth->dev, "seqnum doesn't match, cmd.seqnum %d != rsp.seqnum %d\n",
			fdb_cmd.seqnum, fdb_cmd_rsp.seqnum);
		return -EINVAL;
	}

	if (fdb_cmd_rsp.status == 1)
		return 0;

	return -EINVAL;
}