blob: 2d2329c204ae50ebfe74c974527090c177286bfe [file] [log] [blame]
Horatiu Vultur295e5b92019-04-08 10:31:36 +02001// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
2/*
3 * Copyright (c) 2019 Microsemi Corporation
4 */
5
6#include <common.h>
7#include <config.h>
8#include <dm.h>
Simon Glass9bc15642020-02-03 07:36:16 -07009#include <malloc.h>
Horatiu Vultur295e5b92019-04-08 10:31:36 +020010#include <dm/of_access.h>
11#include <dm/of_addr.h>
12#include <fdt_support.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060013#include <linux/bitops.h>
Horatiu Vultur295e5b92019-04-08 10:31:36 +020014#include <linux/io.h>
15#include <linux/ioport.h>
16#include <miiphy.h>
17#include <net.h>
18#include <wait_bit.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060019#include <linux/printk.h>
Horatiu Vultur295e5b92019-04-08 10:31:36 +020020
21#include "mscc_xfer.h"
Horatiu Vultur6fbf1612019-06-09 15:27:29 +020022#include "mscc_miim.h"
Horatiu Vultur295e5b92019-04-08 10:31:36 +020023
24#define PHY_CFG 0x0
25#define PHY_CFG_ENA 0x3
26#define PHY_CFG_COMMON_RST BIT(2)
27#define PHY_CFG_RST (0x3 << 3)
28#define PHY_STAT 0x4
29#define PHY_STAT_SUPERVISOR_COMPLETE BIT(0)
30
31#define ANA_AC_RAM_CTRL_RAM_INIT 0x14fdc
32#define ANA_AC_STAT_GLOBAL_CFG_PORT_RESET 0x15474
33
34#define ANA_CL_PORT_VLAN_CFG(x) (0xa018 + 0xc8 * (x))
35#define ANA_CL_PORT_VLAN_CFG_AWARE_ENA BIT(19)
36#define ANA_CL_PORT_VLAN_CFG_POP_CNT(x) ((x) << 17)
37
38#define ANA_L2_COMMON_FWD_CFG 0x18498
39#define ANA_L2_COMMON_FWD_CFG_CPU_DMAC_COPY_ENA BIT(6)
40
41#define ASM_CFG_STAT_CFG 0xb08
42#define ASM_CFG_PORT(x) (0xb74 + 0x4 * (x))
43#define ASM_CFG_PORT_NO_PREAMBLE_ENA BIT(8)
44#define ASM_CFG_PORT_INJ_FORMAT_CFG(x) ((x) << 1)
45#define ASM_RAM_CTRL_RAM_INIT 0xbfc
46
47#define DEV_DEV_CFG_DEV_RST_CTRL 0x0
48#define DEV_DEV_CFG_DEV_RST_CTRL_SPEED_SEL(x) ((x) << 20)
49#define DEV_MAC_CFG_MAC_ENA 0x24
50#define DEV_MAC_CFG_MAC_ENA_RX_ENA BIT(4)
51#define DEV_MAC_CFG_MAC_ENA_TX_ENA BIT(0)
52#define DEV_MAC_CFG_MAC_IFG 0x3c
53#define DEV_MAC_CFG_MAC_IFG_TX_IFG(x) ((x) << 8)
54#define DEV_MAC_CFG_MAC_IFG_RX_IFG2(x) ((x) << 4)
55#define DEV_MAC_CFG_MAC_IFG_RX_IFG1(x) (x)
56#define DEV_PCS1G_CFG_PCS1G_CFG 0x48
57#define DEV_PCS1G_CFG_PCS1G_CFG_PCS_ENA BIT(0)
58#define DEV_PCS1G_CFG_PCS1G_MODE 0x4c
59#define DEV_PCS1G_CFG_PCS1G_SD 0x50
60#define DEV_PCS1G_CFG_PCS1G_ANEG 0x54
61#define DEV_PCS1G_CFG_PCS1G_ANEG_ADV_ABILITY(x) ((x) << 16)
62
63#define LRN_COMMON_ACCESS_CTRL 0x0
64#define LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT BIT(0)
65#define LRN_COMMON_MAC_ACCESS_CFG0 0x4
66#define LRN_COMMON_MAC_ACCESS_CFG1 0x8
67#define LRN_COMMON_MAC_ACCESS_CFG2 0xc
68#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_ADDR(x) (x)
69#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_TYPE(x) ((x) << 12)
70#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_VLD BIT(15)
71#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_LOCKED BIT(16)
72#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_COPY BIT(23)
73#define LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_QU(x) ((x) << 24)
74
75#define QFWD_SYSTEM_SWITCH_PORT_MODE(x) (0x4400 + 0x4 * (x))
76#define QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA BIT(17)
77
78#define QS_XTR_GRP_CFG(x) (4 * (x))
79#define QS_INJ_GRP_CFG(x) (0x24 + (x) * 4)
80
81#define QSYS_SYSTEM_RESET_CFG 0x1048
82#define QSYS_CALCFG_CAL_AUTO 0x1134
83#define QSYS_CALCFG_CAL_CTRL 0x113c
84#define QSYS_CALCFG_CAL_CTRL_CAL_MODE(x) ((x) << 11)
85#define QSYS_RAM_CTRL_RAM_INIT 0x1140
86
87#define REW_RAM_CTRL_RAM_INIT 0xFFF4
88
89#define MAC_VID 0
90#define CPU_PORT 11
91#define IFH_LEN 7
92#define ETH_ALEN 6
93#define PGID_BROADCAST 50
94#define PGID_UNICAST 51
95
96static const char * const regs_names[] = {
97 "port0", "port1",
98 "ana_ac", "ana_cl", "ana_l2", "asm", "lrn", "qfwd", "qs", "qsys", "rew",
99};
100
101#define REGS_NAMES_COUNT ARRAY_SIZE(regs_names) + 1
102#define MAX_PORT 2
103
104enum servalt_ctrl_regs {
105 ANA_AC = MAX_PORT,
106 ANA_CL,
107 ANA_L2,
108 ASM,
109 LRN,
110 QFWD,
111 QS,
112 QSYS,
113 REW,
114};
115
116#define SERVALT_MIIM_BUS_COUNT 2
117
118struct servalt_phy_port_t {
119 size_t phy_addr;
120 struct mii_dev *bus;
121};
122
123struct servalt_private {
124 void __iomem *regs[REGS_NAMES_COUNT];
125 struct mii_dev *bus[SERVALT_MIIM_BUS_COUNT];
126 struct servalt_phy_port_t ports[MAX_PORT];
127};
128
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200129static const unsigned long servalt_regs_qs[] = {
130 [MSCC_QS_XTR_RD] = 0x8,
131 [MSCC_QS_XTR_FLUSH] = 0x18,
132 [MSCC_QS_XTR_DATA_PRESENT] = 0x1c,
133 [MSCC_QS_INJ_WR] = 0x2c,
134 [MSCC_QS_INJ_CTRL] = 0x34,
135};
136
137static struct mscc_miim_dev miim[SERVALT_MIIM_BUS_COUNT];
138static int miim_count = -1;
139
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200140static void mscc_phy_reset(void)
141{
142 writel(0, BASE_DEVCPU_GCB + GCB_PHY_CFG + PHY_CFG);
143 writel(PHY_CFG_RST | PHY_CFG_COMMON_RST
144 | PHY_CFG_ENA, BASE_DEVCPU_GCB + GCB_PHY_CFG + PHY_CFG);
145 if (wait_for_bit_le32((const void *)(BASE_DEVCPU_GCB + GCB_PHY_CFG) +
146 PHY_STAT, PHY_STAT_SUPERVISOR_COMPLETE,
147 true, 2000, false)) {
148 pr_err("Timeout in phy reset\n");
149 }
150}
151
152static void servalt_cpu_capture_setup(struct servalt_private *priv)
153{
154 /* ASM: No preamble and IFH prefix on CPU injected frames */
155 writel(ASM_CFG_PORT_NO_PREAMBLE_ENA |
156 ASM_CFG_PORT_INJ_FORMAT_CFG(1),
157 priv->regs[ASM] + ASM_CFG_PORT(CPU_PORT));
158
159 /* Set Manual injection via DEVCPU_QS registers for CPU queue 0 */
160 writel(0x5, priv->regs[QS] + QS_INJ_GRP_CFG(0));
161
162 /* Set Manual extraction via DEVCPU_QS registers for CPU queue 0 */
163 writel(0x7, priv->regs[QS] + QS_XTR_GRP_CFG(0));
164
165 /* Enable CPU port for any frame transfer */
166 setbits_le32(priv->regs[QFWD] + QFWD_SYSTEM_SWITCH_PORT_MODE(CPU_PORT),
167 QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA);
168
169 /* Send a copy to CPU when found as forwarding entry */
170 setbits_le32(priv->regs[ANA_L2] + ANA_L2_COMMON_FWD_CFG,
171 ANA_L2_COMMON_FWD_CFG_CPU_DMAC_COPY_ENA);
172}
173
174static void servalt_port_init(struct servalt_private *priv, int port)
175{
176 void __iomem *regs = priv->regs[port];
177
178 /* Enable PCS */
179 writel(DEV_PCS1G_CFG_PCS1G_CFG_PCS_ENA,
180 regs + DEV_PCS1G_CFG_PCS1G_CFG);
181
182 /* Disable Signal Detect */
183 writel(0, regs + DEV_PCS1G_CFG_PCS1G_SD);
184
185 /* Enable MAC RX and TX */
186 writel(DEV_MAC_CFG_MAC_ENA_RX_ENA |
187 DEV_MAC_CFG_MAC_ENA_TX_ENA,
188 regs + DEV_MAC_CFG_MAC_ENA);
189
190 /* Clear sgmii_mode_ena */
191 writel(0, regs + DEV_PCS1G_CFG_PCS1G_MODE);
192
193 /*
194 * Clear sw_resolve_ena(bit 0) and set adv_ability to
195 * something meaningful just in case
196 */
197 writel(DEV_PCS1G_CFG_PCS1G_ANEG_ADV_ABILITY(0x20),
198 regs + DEV_PCS1G_CFG_PCS1G_ANEG);
199
200 /* Set MAC IFG Gaps */
201 writel(DEV_MAC_CFG_MAC_IFG_TX_IFG(4) |
202 DEV_MAC_CFG_MAC_IFG_RX_IFG1(5) |
203 DEV_MAC_CFG_MAC_IFG_RX_IFG2(1),
204 regs + DEV_MAC_CFG_MAC_IFG);
205
206 /* Set link speed and release all resets */
207 writel(DEV_DEV_CFG_DEV_RST_CTRL_SPEED_SEL(2),
208 regs + DEV_DEV_CFG_DEV_RST_CTRL);
209
210 /* Make VLAN aware for CPU traffic */
211 writel(ANA_CL_PORT_VLAN_CFG_AWARE_ENA |
212 ANA_CL_PORT_VLAN_CFG_POP_CNT(1) |
213 MAC_VID,
214 priv->regs[ANA_CL] + ANA_CL_PORT_VLAN_CFG(port));
215
216 /* Enable CPU port for any frame transfer */
217 setbits_le32(priv->regs[QFWD] + QFWD_SYSTEM_SWITCH_PORT_MODE(port),
218 QFWD_SYSTEM_SWITCH_PORT_MODE_PORT_ENA);
219}
220
221static int ram_init(u32 val, void __iomem *addr)
222{
223 writel(val, addr);
224
225 if (wait_for_bit_le32(addr, BIT(1), false, 2000, false)) {
226 printf("Timeout in memory reset, reg = 0x%08x\n", val);
227 return 1;
228 }
229
230 return 0;
231}
232
233static int servalt_switch_init(struct servalt_private *priv)
234{
235 /* Initialize memories */
236 ram_init(0x3, priv->regs[QSYS] + QSYS_RAM_CTRL_RAM_INIT);
237 ram_init(0x3, priv->regs[ASM] + ASM_RAM_CTRL_RAM_INIT);
238 ram_init(0x3, priv->regs[ANA_AC] + ANA_AC_RAM_CTRL_RAM_INIT);
239 ram_init(0x3, priv->regs[REW] + REW_RAM_CTRL_RAM_INIT);
240
241 /* Reset counters */
242 writel(0x1, priv->regs[ANA_AC] + ANA_AC_STAT_GLOBAL_CFG_PORT_RESET);
243 writel(0x1, priv->regs[ASM] + ASM_CFG_STAT_CFG);
244
245 /* Enable switch-core and queue system */
246 writel(0x1, priv->regs[QSYS] + QSYS_SYSTEM_RESET_CFG);
247
248 return 0;
249}
250
251static void servalt_switch_config(struct servalt_private *priv)
252{
253 writel(0x55555555, priv->regs[QSYS] + QSYS_CALCFG_CAL_AUTO);
254
255 writel(readl(priv->regs[QSYS] + QSYS_CALCFG_CAL_CTRL) |
256 QSYS_CALCFG_CAL_CTRL_CAL_MODE(8),
257 priv->regs[QSYS] + QSYS_CALCFG_CAL_CTRL);
258}
259
260static int servalt_initialize(struct servalt_private *priv)
261{
262 int ret, i;
263
264 /* Initialize switch memories, enable core */
265 ret = servalt_switch_init(priv);
266 if (ret)
267 return ret;
268
269 servalt_switch_config(priv);
270
271 for (i = 0; i < MAX_PORT; i++)
272 servalt_port_init(priv, i);
273
274 servalt_cpu_capture_setup(priv);
275
276 return 0;
277}
278
279static inline
280int servalt_vlant_wait_for_completion(struct servalt_private *priv)
281{
282 if (wait_for_bit_le32(priv->regs[LRN] + LRN_COMMON_ACCESS_CTRL,
283 LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT,
284 false, 2000, false))
285 return -ETIMEDOUT;
286
287 return 0;
288}
289
290static int servalt_mac_table_add(struct servalt_private *priv,
291 const unsigned char mac[ETH_ALEN], int pgid)
292{
293 u32 macl = 0, mach = 0;
294
295 /*
296 * Set the MAC address to handle and the vlan associated in a format
297 * understood by the hardware.
298 */
299 mach |= MAC_VID << 16;
300 mach |= ((u32)mac[0]) << 8;
301 mach |= ((u32)mac[1]) << 0;
302 macl |= ((u32)mac[2]) << 24;
303 macl |= ((u32)mac[3]) << 16;
304 macl |= ((u32)mac[4]) << 8;
305 macl |= ((u32)mac[5]) << 0;
306
307 writel(mach, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG0);
308 writel(macl, priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG1);
309
310 writel(LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_ADDR(pgid) |
311 LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_TYPE(0x3) |
312 LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_COPY |
313 LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_CPU_QU(0) |
314 LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_VLD |
315 LRN_COMMON_MAC_ACCESS_CFG2_MAC_ENTRY_LOCKED,
316 priv->regs[LRN] + LRN_COMMON_MAC_ACCESS_CFG2);
317
318 writel(LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT,
319 priv->regs[LRN] + LRN_COMMON_ACCESS_CTRL);
320
321 return servalt_vlant_wait_for_completion(priv);
322}
323
324static int servalt_write_hwaddr(struct udevice *dev)
325{
326 struct servalt_private *priv = dev_get_priv(dev);
Simon Glassfa20e932020-12-03 16:55:20 -0700327 struct eth_pdata *pdata = dev_get_plat(dev);
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200328
329 return servalt_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST);
330}
331
332static int servalt_start(struct udevice *dev)
333{
334 struct servalt_private *priv = dev_get_priv(dev);
Simon Glassfa20e932020-12-03 16:55:20 -0700335 struct eth_pdata *pdata = dev_get_plat(dev);
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200336 const unsigned char mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff,
337 0xff };
338 int ret;
339
340 ret = servalt_initialize(priv);
341 if (ret)
342 return ret;
343
344 /* Set MAC address tables entries for CPU redirection */
345 ret = servalt_mac_table_add(priv, mac, PGID_BROADCAST);
346 if (ret)
347 return ret;
348
349 ret = servalt_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST);
350 if (ret)
351 return ret;
352
353 return 0;
354}
355
356static void servalt_stop(struct udevice *dev)
357{
358}
359
360static int servalt_send(struct udevice *dev, void *packet, int length)
361{
362 struct servalt_private *priv = dev_get_priv(dev);
363 u32 ifh[IFH_LEN];
364 u32 *buf = packet;
365
366 memset(ifh, '\0', IFH_LEN * 4);
367
368 /* Set DST PORT_MASK */
369 ifh[0] = htonl(0);
370 ifh[1] = htonl(0x1FFFFF);
371 ifh[2] = htonl(~0);
372 /* Set DST_MODE to INJECT and UPDATE_FCS */
373 ifh[5] = htonl(0x4c0);
374
375 return mscc_send(priv->regs[QS], servalt_regs_qs,
376 ifh, IFH_LEN, buf, length);
377}
378
379static int servalt_recv(struct udevice *dev, int flags, uchar **packetp)
380{
381 struct servalt_private *priv = dev_get_priv(dev);
382 u32 *rxbuf = (u32 *)net_rx_packets[0];
383 int byte_cnt = 0;
384
385 byte_cnt = mscc_recv(priv->regs[QS], servalt_regs_qs, rxbuf, IFH_LEN,
386 false);
387
388 *packetp = net_rx_packets[0];
389
390 return byte_cnt;
391}
392
393static struct mii_dev *get_mdiobus(phys_addr_t base, unsigned long size)
394{
395 int i = 0;
396
397 for (i = 0; i < SERVALT_MIIM_BUS_COUNT; ++i)
398 if (miim[i].miim_base == base && miim[i].miim_size == size)
399 return miim[i].bus;
400
401 return NULL;
402}
403
404static void add_port_entry(struct servalt_private *priv, size_t index,
405 size_t phy_addr, struct mii_dev *bus)
406{
407 priv->ports[index].phy_addr = phy_addr;
408 priv->ports[index].bus = bus;
409}
410
411static int servalt_probe(struct udevice *dev)
412{
413 struct servalt_private *priv = dev_get_priv(dev);
414 int i;
415 struct resource res;
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200416 phys_addr_t addr_base;
417 unsigned long addr_size;
418 ofnode eth_node, node, mdio_node;
419 size_t phy_addr;
420 struct mii_dev *bus;
421 struct ofnode_phandle_args phandle;
422
423 if (!priv)
424 return -EINVAL;
425
426 /* Get registers and map them to the private structure */
427 for (i = 0; i < ARRAY_SIZE(regs_names); i++) {
428 priv->regs[i] = dev_remap_addr_name(dev, regs_names[i]);
429 if (!priv->regs[i]) {
430 debug
431 ("Error can't get regs base addresses for %s\n",
432 regs_names[i]);
433 return -ENOMEM;
434 }
435 }
436
437 /* Initialize miim buses */
438 memset(&miim, 0x0, sizeof(struct mscc_miim_dev) *
439 SERVALT_MIIM_BUS_COUNT);
440
441 /* iterate all the ports and find out on which bus they are */
442 i = 0;
443 eth_node = dev_read_first_subnode(dev);
444 for (node = ofnode_first_subnode(eth_node);
445 ofnode_valid(node);
446 node = ofnode_next_subnode(node)) {
447 if (ofnode_read_resource(node, 0, &res))
448 return -ENOMEM;
449 i = res.start;
450
451 ofnode_parse_phandle_with_args(node, "phy-handle", NULL, 0, 0,
452 &phandle);
453
454 /* Get phy address on mdio bus */
455 if (ofnode_read_resource(phandle.node, 0, &res))
456 return -ENOMEM;
457 phy_addr = res.start;
458
459 /* Get mdio node */
460 mdio_node = ofnode_get_parent(phandle.node);
461
462 if (ofnode_read_resource(mdio_node, 0, &res))
463 return -ENOMEM;
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200464
Patrick Delaunay2a28c6e2021-04-06 09:38:06 +0200465 addr_base = res.start;
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200466 addr_size = res.end - res.start;
467
468 /* If the bus is new then create a new bus */
469 if (!get_mdiobus(addr_base, addr_size))
470 priv->bus[miim_count] =
Horatiu Vultur6fbf1612019-06-09 15:27:29 +0200471 mscc_mdiobus_init(miim, &miim_count, addr_base,
472 addr_size);
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200473
474 /* Connect mdio bus with the port */
475 bus = get_mdiobus(addr_base, addr_size);
476 add_port_entry(priv, i, phy_addr, bus);
477 }
478
479 mscc_phy_reset();
480
481 for (i = 0; i < MAX_PORT; i++) {
482 if (!priv->ports[i].bus)
483 continue;
484
485 phy_connect(priv->ports[i].bus, priv->ports[i].phy_addr, dev,
Marek BehĂșn48631e42022-04-07 00:33:03 +0200486 PHY_INTERFACE_MODE_NA);
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200487 }
488
489 return 0;
490}
491
492static int servalt_remove(struct udevice *dev)
493{
494 struct servalt_private *priv = dev_get_priv(dev);
495 int i;
496
497 for (i = 0; i < SERVALT_MIIM_BUS_COUNT; i++) {
498 mdio_unregister(priv->bus[i]);
499 mdio_free(priv->bus[i]);
500 }
501
502 return 0;
503}
504
505static const struct eth_ops servalt_ops = {
506 .start = servalt_start,
507 .stop = servalt_stop,
508 .send = servalt_send,
509 .recv = servalt_recv,
510 .write_hwaddr = servalt_write_hwaddr,
511};
512
513static const struct udevice_id mscc_servalt_ids[] = {
514 {.compatible = "mscc,vsc7437-switch" },
515 { /* Sentinel */ }
516};
517
518U_BOOT_DRIVER(servalt) = {
519 .name = "servalt-switch",
520 .id = UCLASS_ETH,
521 .of_match = mscc_servalt_ids,
522 .probe = servalt_probe,
523 .remove = servalt_remove,
524 .ops = &servalt_ops,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700525 .priv_auto = sizeof(struct servalt_private),
Simon Glass71fa5b42020-12-03 16:55:18 -0700526 .plat_auto = sizeof(struct eth_pdata),
Horatiu Vultur295e5b92019-04-08 10:31:36 +0200527};