Commit c8546163fa92bb46c09017db18dadbb26e639895
Committed by
Daniel Schwierzeck
1 parent
55037902b8
Exists in
smarc_8mq_lf_v2020.04
and in
11 other branches
net: add MSCC Ocelot switch support
This patch adds support for the Microsemi Ethernet switch present on Ocelot SoCs. Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com> Reviewed-by: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
Showing 4 changed files with 774 additions and 0 deletions Side-by-side Diff
MAINTAINERS
drivers/net/Kconfig
... | ... | @@ -432,6 +432,13 @@ |
432 | 432 | This driver implements support for the Socionext AVE Ethernet |
433 | 433 | controller, as found on the Socionext UniPhier family. |
434 | 434 | |
435 | +config MSCC_OCELOT_SWITCH | |
436 | + bool "Ocelot switch driver" | |
437 | + depends on DM_ETH && ARCH_MSCC | |
438 | + select PHYLIB | |
439 | + help | |
440 | + This driver supports the Ocelot network switch device. | |
441 | + | |
435 | 442 | config ETHER_ON_FEC1 |
436 | 443 | bool "FEC1" |
437 | 444 | depends on MPC8XX_FEC |
drivers/net/Makefile
drivers/net/ocelot_switch.c
1 | +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) | |
2 | +/* | |
3 | + * Copyright (c) 2018 Microsemi Corporation | |
4 | + */ | |
5 | + | |
6 | +#include <common.h> | |
7 | +#include <config.h> | |
8 | +#include <dm.h> | |
9 | +#include <dm/of_access.h> | |
10 | +#include <dm/of_addr.h> | |
11 | +#include <fdt_support.h> | |
12 | +#include <linux/io.h> | |
13 | +#include <linux/ioport.h> | |
14 | +#include <miiphy.h> | |
15 | +#include <net.h> | |
16 | +#include <wait_bit.h> | |
17 | + | |
18 | +#define MIIM_STATUS 0x0 | |
19 | +#define MIIM_STAT_BUSY BIT(3) | |
20 | +#define MIIM_CMD 0x8 | |
21 | +#define MIIM_CMD_SCAN BIT(0) | |
22 | +#define MIIM_CMD_OPR_WRITE BIT(1) | |
23 | +#define MIIM_CMD_OPR_READ BIT(2) | |
24 | +#define MIIM_CMD_SINGLE_SCAN BIT(3) | |
25 | +#define MIIM_CMD_WRDATA(x) ((x) << 4) | |
26 | +#define MIIM_CMD_REGAD(x) ((x) << 20) | |
27 | +#define MIIM_CMD_PHYAD(x) ((x) << 25) | |
28 | +#define MIIM_CMD_VLD BIT(31) | |
29 | +#define MIIM_DATA 0xC | |
30 | +#define MIIM_DATA_ERROR (0x2 << 16) | |
31 | + | |
32 | +#define PHY_CFG 0x0 | |
33 | +#define PHY_CFG_ENA 0xF | |
34 | +#define PHY_CFG_COMMON_RST BIT(4) | |
35 | +#define PHY_CFG_RST (0xF << 5) | |
36 | +#define PHY_STAT 0x4 | |
37 | +#define PHY_STAT_SUPERVISOR_COMPLETE BIT(0) | |
38 | + | |
39 | +#define ANA_PORT_VLAN_CFG(x) (0x7000 + 0x100 * (x)) | |
40 | +#define ANA_PORT_VLAN_CFG_AWARE_ENA BIT(20) | |
41 | +#define ANA_PORT_VLAN_CFG_POP_CNT(x) ((x) << 18) | |
42 | +#define ANA_PORT_PORT_CFG(x) (0x7070 + 0x100 * (x)) | |
43 | +#define ANA_PORT_PORT_CFG_RECV_ENA BIT(6) | |
44 | +#define ANA_TABLES_MACHDATA 0x8b34 | |
45 | +#define ANA_TABLES_MACLDATA 0x8b38 | |
46 | +#define ANA_TABLES_MACACCESS 0x8b3c | |
47 | +#define ANA_TABLES_MACACCESS_VALID BIT(11) | |
48 | +#define ANA_TABLES_MACACCESS_ENTRYTYPE(x) ((x) << 9) | |
49 | +#define ANA_TABLES_MACACCESS_DEST_IDX(x) ((x) << 3) | |
50 | +#define ANA_TABLES_MACACCESS_MAC_TABLE_CMD(x) (x) | |
51 | +#define ANA_TABLES_MACACCESS_MAC_TABLE_CMD_M GENMASK(2, 0) | |
52 | +#define MACACCESS_CMD_IDLE 0 | |
53 | +#define MACACCESS_CMD_LEARN 1 | |
54 | +#define MACACCESS_CMD_GET_NEXT 4 | |
55 | +#define ANA_PGID(x) (0x8c00 + 4 * (x)) | |
56 | + | |
57 | +#define SYS_FRM_AGING 0x574 | |
58 | +#define SYS_FRM_AGING_ENA BIT(20) | |
59 | + | |
60 | +#define SYS_SYSTEM_RST_CFG 0x508 | |
61 | +#define SYS_SYSTEM_RST_MEM_INIT BIT(0) | |
62 | +#define SYS_SYSTEM_RST_MEM_ENA BIT(1) | |
63 | +#define SYS_SYSTEM_RST_CORE_ENA BIT(2) | |
64 | +#define SYS_PORT_MODE(x) (0x514 + 0x4 * (x)) | |
65 | +#define SYS_PORT_MODE_INCL_INJ_HDR(x) ((x) << 3) | |
66 | +#define SYS_PORT_MODE_INCL_INJ_HDR_M GENMASK(4, 3) | |
67 | +#define SYS_PORT_MODE_INCL_XTR_HDR(x) ((x) << 1) | |
68 | +#define SYS_PORT_MODE_INCL_XTR_HDR_M GENMASK(2, 1) | |
69 | +#define SYS_PAUSE_CFG(x) (0x608 + 0x4 * (x)) | |
70 | +#define SYS_PAUSE_CFG_PAUSE_ENA BIT(0) | |
71 | + | |
72 | +#define QSYS_SWITCH_PORT_MODE(x) (0x11234 + 0x4 * (x)) | |
73 | +#define QSYS_SWITCH_PORT_MODE_PORT_ENA BIT(14) | |
74 | +#define QSYS_QMAP 0x112d8 | |
75 | +#define QSYS_EGR_NO_SHARING 0x1129c | |
76 | + | |
77 | +/* Port registers */ | |
78 | +#define DEV_CLOCK_CFG 0x0 | |
79 | +#define DEV_CLOCK_CFG_LINK_SPEED_1000 1 | |
80 | +#define DEV_MAC_ENA_CFG 0x1c | |
81 | +#define DEV_MAC_ENA_CFG_RX_ENA BIT(4) | |
82 | +#define DEV_MAC_ENA_CFG_TX_ENA BIT(0) | |
83 | + | |
84 | +#define DEV_MAC_IFG_CFG 0x30 | |
85 | +#define DEV_MAC_IFG_CFG_TX_IFG(x) ((x) << 8) | |
86 | +#define DEV_MAC_IFG_CFG_RX_IFG2(x) ((x) << 4) | |
87 | +#define DEV_MAC_IFG_CFG_RX_IFG1(x) (x) | |
88 | + | |
89 | +#define PCS1G_CFG 0x48 | |
90 | +#define PCS1G_MODE_CFG_SGMII_MODE_ENA BIT(0) | |
91 | +#define PCS1G_MODE_CFG 0x4c | |
92 | +#define PCS1G_MODE_CFG_UNIDIR_MODE_ENA BIT(4) | |
93 | +#define PCS1G_MODE_CFG_SGMII_MODE_ENA BIT(0) | |
94 | +#define PCS1G_SD_CFG 0x50 | |
95 | +#define PCS1G_ANEG_CFG 0x54 | |
96 | +#define PCS1G_ANEG_CFG_ADV_ABILITY(x) ((x) << 16) | |
97 | + | |
98 | +#define QS_XTR_GRP_CFG(x) (4 * (x)) | |
99 | +#define QS_XTR_GRP_CFG_MODE(x) ((x) << 2) | |
100 | +#define QS_XTR_GRP_CFG_STATUS_WORD_POS BIT(1) | |
101 | +#define QS_XTR_GRP_CFG_BYTE_SWAP BIT(0) | |
102 | +#define QS_XTR_RD(x) (0x8 + 4 * (x)) | |
103 | +#define QS_XTR_FLUSH 0x18 | |
104 | +#define QS_XTR_FLUSH_FLUSH GENMASK(1, 0) | |
105 | +#define QS_XTR_DATA_PRESENT 0x1c | |
106 | +#define QS_INJ_GRP_CFG(x) (0x24 + (x) * 4) | |
107 | +#define QS_INJ_GRP_CFG_MODE(x) ((x) << 2) | |
108 | +#define QS_INJ_GRP_CFG_BYTE_SWAP BIT(0) | |
109 | +#define QS_INJ_WR(x) (0x2c + 4 * (x)) | |
110 | +#define QS_INJ_CTRL(x) (0x34 + 4 * (x)) | |
111 | +#define QS_INJ_CTRL_GAP_SIZE(x) ((x) << 21) | |
112 | +#define QS_INJ_CTRL_EOF BIT(19) | |
113 | +#define QS_INJ_CTRL_SOF BIT(18) | |
114 | +#define QS_INJ_CTRL_VLD_BYTES(x) ((x) << 16) | |
115 | + | |
116 | +#define XTR_EOF_0 ntohl(0x80000000u) | |
117 | +#define XTR_EOF_1 ntohl(0x80000001u) | |
118 | +#define XTR_EOF_2 ntohl(0x80000002u) | |
119 | +#define XTR_EOF_3 ntohl(0x80000003u) | |
120 | +#define XTR_PRUNED ntohl(0x80000004u) | |
121 | +#define XTR_ABORT ntohl(0x80000005u) | |
122 | +#define XTR_ESCAPE ntohl(0x80000006u) | |
123 | +#define XTR_NOT_READY ntohl(0x80000007u) | |
124 | + | |
125 | +#define IFH_INJ_BYPASS BIT(31) | |
126 | +#define IFH_TAG_TYPE_C 0 | |
127 | +#define XTR_VALID_BYTES(x) (4 - ((x) & 3)) | |
128 | +#define MAC_VID 1 | |
129 | +#define CPU_PORT 11 | |
130 | +#define INTERNAL_PORT_MSK 0xF | |
131 | +#define IFH_LEN 4 | |
132 | +#define OCELOT_BUF_CELL_SZ 60 | |
133 | +#define ETH_ALEN 6 | |
134 | +#define PGID_BROADCAST 13 | |
135 | +#define PGID_UNICAST 14 | |
136 | +#define PGID_SRC 80 | |
137 | + | |
138 | +enum ocelot_target { | |
139 | + ANA, | |
140 | + QS, | |
141 | + QSYS, | |
142 | + REW, | |
143 | + SYS, | |
144 | + HSIO, | |
145 | + PORT0, | |
146 | + PORT1, | |
147 | + PORT2, | |
148 | + PORT3, | |
149 | + TARGET_MAX, | |
150 | +}; | |
151 | + | |
152 | +#define MAX_PORT (PORT3 - PORT0) | |
153 | + | |
154 | +/* MAC table entry types. | |
155 | + * ENTRYTYPE_NORMAL is subject to aging. | |
156 | + * ENTRYTYPE_LOCKED is not subject to aging. | |
157 | + * ENTRYTYPE_MACv4 is not subject to aging. For IPv4 multicast. | |
158 | + * ENTRYTYPE_MACv6 is not subject to aging. For IPv6 multicast. | |
159 | + */ | |
160 | +enum macaccess_entry_type { | |
161 | + ENTRYTYPE_NORMAL = 0, | |
162 | + ENTRYTYPE_LOCKED, | |
163 | + ENTRYTYPE_MACv4, | |
164 | + ENTRYTYPE_MACv6, | |
165 | +}; | |
166 | + | |
167 | +enum ocelot_mdio_target { | |
168 | + MIIM, | |
169 | + PHY, | |
170 | + TARGET_MDIO_MAX, | |
171 | +}; | |
172 | + | |
173 | +enum ocelot_phy_id { | |
174 | + INTERNAL, | |
175 | + EXTERNAL, | |
176 | + NUM_PHY, | |
177 | +}; | |
178 | + | |
179 | +struct ocelot_private { | |
180 | + void __iomem *regs[TARGET_MAX]; | |
181 | + | |
182 | + struct mii_dev *bus[NUM_PHY]; | |
183 | + struct phy_device *phydev; | |
184 | + int phy_mode; | |
185 | + int max_speed; | |
186 | + | |
187 | + int rx_pos; | |
188 | + int rx_siz; | |
189 | + int rx_off; | |
190 | + int tx_num; | |
191 | + | |
192 | + u8 tx_adj_packetbuf[PKTSIZE_ALIGN + PKTALIGN]; | |
193 | + void *tx_adj_buf; | |
194 | +}; | |
195 | + | |
196 | +struct mscc_miim_dev { | |
197 | + void __iomem *regs; | |
198 | + void __iomem *phy_regs; | |
199 | +}; | |
200 | + | |
201 | +struct mscc_miim_dev miim[NUM_PHY]; | |
202 | + | |
203 | +static int mscc_miim_wait_ready(struct mscc_miim_dev *miim) | |
204 | +{ | |
205 | + return wait_for_bit_le32(miim->regs + MIIM_STATUS, MIIM_STAT_BUSY, | |
206 | + false, 250, false); | |
207 | +} | |
208 | + | |
209 | +static int mscc_miim_reset(struct mii_dev *bus) | |
210 | +{ | |
211 | + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; | |
212 | + | |
213 | + if (miim->phy_regs) { | |
214 | + writel(0, miim->phy_regs + PHY_CFG); | |
215 | + writel(PHY_CFG_RST | PHY_CFG_COMMON_RST | |
216 | + | PHY_CFG_ENA, miim->phy_regs + PHY_CFG); | |
217 | + mdelay(500); | |
218 | + } | |
219 | + | |
220 | + return 0; | |
221 | +} | |
222 | + | |
223 | +static int mscc_miim_read(struct mii_dev *bus, int addr, int devad, int reg) | |
224 | +{ | |
225 | + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; | |
226 | + u32 val; | |
227 | + int ret; | |
228 | + | |
229 | + ret = mscc_miim_wait_ready(miim); | |
230 | + if (ret) | |
231 | + goto out; | |
232 | + | |
233 | + writel(MIIM_CMD_VLD | MIIM_CMD_PHYAD(addr) | | |
234 | + MIIM_CMD_REGAD(reg) | MIIM_CMD_OPR_READ, | |
235 | + miim->regs + MIIM_CMD); | |
236 | + | |
237 | + ret = mscc_miim_wait_ready(miim); | |
238 | + if (ret) | |
239 | + goto out; | |
240 | + | |
241 | + val = readl(miim->regs + MIIM_DATA); | |
242 | + if (val & MIIM_DATA_ERROR) { | |
243 | + ret = -EIO; | |
244 | + goto out; | |
245 | + } | |
246 | + | |
247 | + ret = val & 0xFFFF; | |
248 | + out: | |
249 | + return ret; | |
250 | +} | |
251 | + | |
252 | +static int mscc_miim_write(struct mii_dev *bus, int addr, int devad, int reg, | |
253 | + u16 val) | |
254 | +{ | |
255 | + struct mscc_miim_dev *miim = (struct mscc_miim_dev *)bus->priv; | |
256 | + int ret; | |
257 | + | |
258 | + ret = mscc_miim_wait_ready(miim); | |
259 | + if (ret < 0) | |
260 | + goto out; | |
261 | + | |
262 | + writel(MIIM_CMD_VLD | MIIM_CMD_PHYAD(addr) | | |
263 | + MIIM_CMD_REGAD(reg) | MIIM_CMD_WRDATA(val) | | |
264 | + MIIM_CMD_OPR_WRITE, miim->regs + MIIM_CMD); | |
265 | + out: | |
266 | + return ret; | |
267 | +} | |
268 | + | |
269 | +/* For now only setup the internal mdio bus */ | |
270 | +static struct mii_dev *ocelot_mdiobus_init(struct udevice *dev) | |
271 | +{ | |
272 | + unsigned long phy_size[TARGET_MAX]; | |
273 | + phys_addr_t phy_base[TARGET_MAX]; | |
274 | + struct ofnode_phandle_args phandle; | |
275 | + ofnode eth_node, node, mdio_node; | |
276 | + struct resource res; | |
277 | + struct mii_dev *bus; | |
278 | + fdt32_t faddr; | |
279 | + int i; | |
280 | + | |
281 | + bus = mdio_alloc(); | |
282 | + | |
283 | + if (!bus) | |
284 | + return NULL; | |
285 | + | |
286 | + /* gathered only the first mdio bus */ | |
287 | + eth_node = dev_read_first_subnode(dev); | |
288 | + node = ofnode_first_subnode(eth_node); | |
289 | + ofnode_parse_phandle_with_args(node, "phy-handle", NULL, 0, 0, | |
290 | + &phandle); | |
291 | + mdio_node = ofnode_get_parent(phandle.node); | |
292 | + | |
293 | + for (i = 0; i < TARGET_MDIO_MAX; i++) { | |
294 | + if (ofnode_read_resource(mdio_node, i, &res)) { | |
295 | + pr_err("%s: get OF resource failed\n", __func__); | |
296 | + return NULL; | |
297 | + } | |
298 | + faddr = cpu_to_fdt32(res.start); | |
299 | + phy_base[i] = ofnode_translate_address(mdio_node, &faddr); | |
300 | + phy_size[i] = res.end - res.start; | |
301 | + } | |
302 | + | |
303 | + strcpy(bus->name, "miim-internal"); | |
304 | + miim[INTERNAL].phy_regs = ioremap(phy_base[PHY], phy_size[PHY]); | |
305 | + miim[INTERNAL].regs = ioremap(phy_base[MIIM], phy_size[MIIM]); | |
306 | + bus->priv = &miim[INTERNAL]; | |
307 | + bus->reset = mscc_miim_reset; | |
308 | + bus->read = mscc_miim_read; | |
309 | + bus->write = mscc_miim_write; | |
310 | + | |
311 | + if (mdio_register(bus)) | |
312 | + return NULL; | |
313 | + else | |
314 | + return bus; | |
315 | +} | |
316 | + | |
317 | +__weak void mscc_switch_reset(void) | |
318 | +{ | |
319 | +} | |
320 | + | |
321 | +static void ocelot_stop(struct udevice *dev) | |
322 | +{ | |
323 | + struct ocelot_private *priv = dev_get_priv(dev); | |
324 | + int i; | |
325 | + | |
326 | + mscc_switch_reset(); | |
327 | + for (i = 0; i < NUM_PHY; i++) | |
328 | + if (priv->bus[i]) | |
329 | + mscc_miim_reset(priv->bus[i]); | |
330 | +} | |
331 | + | |
332 | +static void ocelot_cpu_capture_setup(struct ocelot_private *priv) | |
333 | +{ | |
334 | + int i; | |
335 | + | |
336 | + /* map the 8 CPU extraction queues to CPU port 11 */ | |
337 | + writel(0, priv->regs[QSYS] + QSYS_QMAP); | |
338 | + | |
339 | + for (i = 0; i <= 1; i++) { | |
340 | + /* | |
341 | + * Do byte-swap and expect status after last data word | |
342 | + * Extraction: Mode: manual extraction) | Byte_swap | |
343 | + */ | |
344 | + writel(QS_XTR_GRP_CFG_MODE(1) | QS_XTR_GRP_CFG_BYTE_SWAP, | |
345 | + priv->regs[QS] + QS_XTR_GRP_CFG(i)); | |
346 | + /* | |
347 | + * Injection: Mode: manual extraction | Byte_swap | |
348 | + */ | |
349 | + writel(QS_INJ_GRP_CFG_MODE(1) | QS_INJ_GRP_CFG_BYTE_SWAP, | |
350 | + priv->regs[QS] + QS_INJ_GRP_CFG(i)); | |
351 | + } | |
352 | + | |
353 | + for (i = 0; i <= 1; i++) | |
354 | + /* Enable IFH insertion/parsing on CPU ports */ | |
355 | + writel(SYS_PORT_MODE_INCL_INJ_HDR(1) | | |
356 | + SYS_PORT_MODE_INCL_XTR_HDR(1), | |
357 | + priv->regs[SYS] + SYS_PORT_MODE(CPU_PORT + i)); | |
358 | + /* | |
359 | + * Setup the CPU port as VLAN aware to support switching frames | |
360 | + * based on tags | |
361 | + */ | |
362 | + writel(ANA_PORT_VLAN_CFG_AWARE_ENA | ANA_PORT_VLAN_CFG_POP_CNT(1) | | |
363 | + MAC_VID, priv->regs[ANA] + ANA_PORT_VLAN_CFG(CPU_PORT)); | |
364 | + | |
365 | + /* Disable learning (only RECV_ENA must be set) */ | |
366 | + writel(ANA_PORT_PORT_CFG_RECV_ENA, | |
367 | + priv->regs[ANA] + ANA_PORT_PORT_CFG(CPU_PORT)); | |
368 | + | |
369 | + /* Enable switching to/from cpu port */ | |
370 | + setbits_le32(priv->regs[QSYS] + QSYS_SWITCH_PORT_MODE(CPU_PORT), | |
371 | + QSYS_SWITCH_PORT_MODE_PORT_ENA); | |
372 | + | |
373 | + /* No pause on CPU port - not needed (off by default) */ | |
374 | + clrbits_le32(priv->regs[SYS] + SYS_PAUSE_CFG(CPU_PORT), | |
375 | + SYS_PAUSE_CFG_PAUSE_ENA); | |
376 | + | |
377 | + setbits_le32(priv->regs[QSYS] + QSYS_EGR_NO_SHARING, BIT(CPU_PORT)); | |
378 | +} | |
379 | + | |
380 | +static void ocelot_port_init(struct ocelot_private *priv, int port) | |
381 | +{ | |
382 | + void __iomem *regs = priv->regs[port]; | |
383 | + | |
384 | + /* Enable PCS */ | |
385 | + writel(PCS1G_MODE_CFG_SGMII_MODE_ENA, regs + PCS1G_CFG); | |
386 | + | |
387 | + /* Disable Signal Detect */ | |
388 | + writel(0, regs + PCS1G_SD_CFG); | |
389 | + | |
390 | + /* Enable MAC RX and TX */ | |
391 | + writel(DEV_MAC_ENA_CFG_RX_ENA | DEV_MAC_ENA_CFG_TX_ENA, | |
392 | + regs + DEV_MAC_ENA_CFG); | |
393 | + | |
394 | + /* Clear sgmii_mode_ena */ | |
395 | + writel(0, regs + PCS1G_MODE_CFG); | |
396 | + | |
397 | + /* | |
398 | + * Clear sw_resolve_ena(bit 0) and set adv_ability to | |
399 | + * something meaningful just in case | |
400 | + */ | |
401 | + writel(PCS1G_ANEG_CFG_ADV_ABILITY(0x20), regs + PCS1G_ANEG_CFG); | |
402 | + | |
403 | + /* Set MAC IFG Gaps */ | |
404 | + writel(DEV_MAC_IFG_CFG_TX_IFG(5) | DEV_MAC_IFG_CFG_RX_IFG1(5) | | |
405 | + DEV_MAC_IFG_CFG_RX_IFG2(1), regs + DEV_MAC_IFG_CFG); | |
406 | + | |
407 | + /* Set link speed and release all resets */ | |
408 | + writel(DEV_CLOCK_CFG_LINK_SPEED_1000, regs + DEV_CLOCK_CFG); | |
409 | + | |
410 | + /* Make VLAN aware for CPU traffic */ | |
411 | + writel(ANA_PORT_VLAN_CFG_AWARE_ENA | ANA_PORT_VLAN_CFG_POP_CNT(1) | | |
412 | + MAC_VID, priv->regs[ANA] + ANA_PORT_VLAN_CFG(port - PORT0)); | |
413 | + | |
414 | + /* Enable the port in the core */ | |
415 | + setbits_le32(priv->regs[QSYS] + QSYS_SWITCH_PORT_MODE(port - PORT0), | |
416 | + QSYS_SWITCH_PORT_MODE_PORT_ENA); | |
417 | +} | |
418 | + | |
419 | +static int ocelot_switch_init(struct ocelot_private *priv) | |
420 | +{ | |
421 | + /* Reset switch & memories */ | |
422 | + writel(SYS_SYSTEM_RST_MEM_ENA | SYS_SYSTEM_RST_MEM_INIT, | |
423 | + priv->regs[SYS] + SYS_SYSTEM_RST_CFG); | |
424 | + | |
425 | + /* Wait to complete */ | |
426 | + if (wait_for_bit_le32(priv->regs[SYS] + SYS_SYSTEM_RST_CFG, | |
427 | + SYS_SYSTEM_RST_MEM_INIT, false, 2000, false)) { | |
428 | + pr_err("Timeout in memory reset\n"); | |
429 | + return -EIO; | |
430 | + } | |
431 | + | |
432 | + /* Enable switch core */ | |
433 | + setbits_le32(priv->regs[SYS] + SYS_SYSTEM_RST_CFG, | |
434 | + SYS_SYSTEM_RST_CORE_ENA); | |
435 | + | |
436 | + return 0; | |
437 | +} | |
438 | + | |
439 | +static void ocelot_switch_flush(struct ocelot_private *priv) | |
440 | +{ | |
441 | + /* All Queues flush */ | |
442 | + setbits_le32(priv->regs[QS] + QS_XTR_FLUSH, QS_XTR_FLUSH_FLUSH); | |
443 | + /* Allow to drain */ | |
444 | + mdelay(1); | |
445 | + /* All Queues normal */ | |
446 | + clrbits_le32(priv->regs[QS] + QS_XTR_FLUSH, QS_XTR_FLUSH_FLUSH); | |
447 | +} | |
448 | + | |
449 | +static int ocelot_initialize(struct ocelot_private *priv) | |
450 | +{ | |
451 | + int ret, i; | |
452 | + | |
453 | + /* Initialize switch memories, enable core */ | |
454 | + ret = ocelot_switch_init(priv); | |
455 | + if (ret) | |
456 | + return ret; | |
457 | + /* | |
458 | + * Disable port-to-port by switching | |
459 | + * Put fron ports in "port isolation modes" - i.e. they cant send | |
460 | + * to other ports - via the PGID sorce masks. | |
461 | + */ | |
462 | + for (i = 0; i <= MAX_PORT; i++) | |
463 | + writel(0, priv->regs[ANA] + ANA_PGID(PGID_SRC + i)); | |
464 | + | |
465 | + /* Flush queues */ | |
466 | + ocelot_switch_flush(priv); | |
467 | + | |
468 | + /* Setup frame ageing - "2 sec" - The unit is 6.5us on Ocelot */ | |
469 | + writel(SYS_FRM_AGING_ENA | (20000000 / 65), | |
470 | + priv->regs[SYS] + SYS_FRM_AGING); | |
471 | + | |
472 | + for (i = PORT0; i <= PORT3; i++) | |
473 | + ocelot_port_init(priv, i); | |
474 | + | |
475 | + ocelot_cpu_capture_setup(priv); | |
476 | + | |
477 | + debug("Ports enabled\n"); | |
478 | + | |
479 | + return 0; | |
480 | +} | |
481 | + | |
482 | +static inline int ocelot_vlant_wait_for_completion(struct ocelot_private *priv) | |
483 | +{ | |
484 | + unsigned int val, timeout = 10; | |
485 | + | |
486 | + /* Wait for the issued mac table command to be completed, or timeout. | |
487 | + * When the command read from ANA_TABLES_MACACCESS is | |
488 | + * MACACCESS_CMD_IDLE, the issued command completed successfully. | |
489 | + */ | |
490 | + do { | |
491 | + val = readl(priv->regs[ANA] + ANA_TABLES_MACACCESS); | |
492 | + val &= ANA_TABLES_MACACCESS_MAC_TABLE_CMD_M; | |
493 | + } while (val != MACACCESS_CMD_IDLE && timeout--); | |
494 | + | |
495 | + if (!timeout) | |
496 | + return -ETIMEDOUT; | |
497 | + | |
498 | + return 0; | |
499 | +} | |
500 | + | |
501 | +static int ocelot_mac_table_add(struct ocelot_private *priv, | |
502 | + const unsigned char mac[ETH_ALEN], int pgid) | |
503 | +{ | |
504 | + u32 macl = 0, mach = 0; | |
505 | + int ret; | |
506 | + | |
507 | + /* Set the MAC address to handle and the vlan associated in a format | |
508 | + * understood by the hardware. | |
509 | + */ | |
510 | + mach |= MAC_VID << 16; | |
511 | + mach |= ((u32)mac[0]) << 8; | |
512 | + mach |= ((u32)mac[1]) << 0; | |
513 | + macl |= ((u32)mac[2]) << 24; | |
514 | + macl |= ((u32)mac[3]) << 16; | |
515 | + macl |= ((u32)mac[4]) << 8; | |
516 | + macl |= ((u32)mac[5]) << 0; | |
517 | + | |
518 | + writel(macl, priv->regs[ANA] + ANA_TABLES_MACLDATA); | |
519 | + writel(mach, priv->regs[ANA] + ANA_TABLES_MACHDATA); | |
520 | + | |
521 | + writel(ANA_TABLES_MACACCESS_VALID | | |
522 | + ANA_TABLES_MACACCESS_DEST_IDX(pgid) | | |
523 | + ANA_TABLES_MACACCESS_ENTRYTYPE(ENTRYTYPE_LOCKED) | | |
524 | + ANA_TABLES_MACACCESS_MAC_TABLE_CMD(MACACCESS_CMD_LEARN), | |
525 | + priv->regs[ANA] + ANA_TABLES_MACACCESS); | |
526 | + | |
527 | + ret = ocelot_vlant_wait_for_completion(priv); | |
528 | + | |
529 | + return ret; | |
530 | +} | |
531 | + | |
532 | +static int ocelot_write_hwaddr(struct udevice *dev) | |
533 | +{ | |
534 | + struct ocelot_private *priv = dev_get_priv(dev); | |
535 | + struct eth_pdata *pdata = dev_get_platdata(dev); | |
536 | + | |
537 | + ocelot_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST); | |
538 | + | |
539 | + writel(BIT(CPU_PORT), priv->regs[ANA] + ANA_PGID(PGID_UNICAST)); | |
540 | + | |
541 | + return 0; | |
542 | +} | |
543 | + | |
544 | +static int ocelot_start(struct udevice *dev) | |
545 | +{ | |
546 | + struct ocelot_private *priv = dev_get_priv(dev); | |
547 | + struct eth_pdata *pdata = dev_get_platdata(dev); | |
548 | + const unsigned char mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, | |
549 | + 0xff }; | |
550 | + int ret; | |
551 | + | |
552 | + ret = ocelot_initialize(priv); | |
553 | + if (ret) | |
554 | + return ret; | |
555 | + | |
556 | + /* Set MAC address tables entries for CPU redirection */ | |
557 | + ocelot_mac_table_add(priv, mac, PGID_BROADCAST); | |
558 | + | |
559 | + writel(BIT(CPU_PORT) | INTERNAL_PORT_MSK, | |
560 | + priv->regs[ANA] + ANA_PGID(PGID_BROADCAST)); | |
561 | + | |
562 | + /* It should be setup latter in ocelot_write_hwaddr */ | |
563 | + ocelot_mac_table_add(priv, pdata->enetaddr, PGID_UNICAST); | |
564 | + | |
565 | + writel(BIT(CPU_PORT), priv->regs[ANA] + ANA_PGID(PGID_UNICAST)); | |
566 | + | |
567 | + return 0; | |
568 | +} | |
569 | + | |
570 | +static int ocelot_send(struct udevice *dev, void *packet, int length) | |
571 | +{ | |
572 | + struct ocelot_private *priv = dev_get_priv(dev); | |
573 | + u32 ifh[IFH_LEN]; | |
574 | + int port = BIT(0); /* use port 0 */ | |
575 | + u8 grp = 0; /* Send everything on CPU group 0 */ | |
576 | + int i, count = (length + 3) / 4, last = length % 4; | |
577 | + u32 *buf = packet; | |
578 | + | |
579 | + writel(QS_INJ_CTRL_GAP_SIZE(1) | QS_INJ_CTRL_SOF, | |
580 | + priv->regs[QS] + QS_INJ_CTRL(grp)); | |
581 | + | |
582 | + /* | |
583 | + * Generate the IFH for frame injection | |
584 | + * | |
585 | + * The IFH is a 128bit-value | |
586 | + * bit 127: bypass the analyzer processing | |
587 | + * bit 56-67: destination mask | |
588 | + * bit 28-29: pop_cnt: 3 disables all rewriting of the frame | |
589 | + * bit 20-27: cpu extraction queue mask | |
590 | + * bit 16: tag type 0: C-tag, 1: S-tag | |
591 | + * bit 0-11: VID | |
592 | + */ | |
593 | + ifh[0] = IFH_INJ_BYPASS; | |
594 | + ifh[1] = (0xf00 & port) >> 8; | |
595 | + ifh[2] = (0xff & port) << 24; | |
596 | + ifh[3] = (IFH_TAG_TYPE_C << 16); | |
597 | + | |
598 | + for (i = 0; i < IFH_LEN; i++) | |
599 | + writel(ifh[i], priv->regs[QS] + QS_INJ_WR(grp)); | |
600 | + | |
601 | + for (i = 0; i < count; i++) | |
602 | + writel(buf[i], priv->regs[QS] + QS_INJ_WR(grp)); | |
603 | + | |
604 | + /* Add padding */ | |
605 | + while (i < (OCELOT_BUF_CELL_SZ / 4)) { | |
606 | + writel(0, priv->regs[QS] + QS_INJ_WR(grp)); | |
607 | + i++; | |
608 | + } | |
609 | + | |
610 | + /* Indicate EOF and valid bytes in last word */ | |
611 | + writel(QS_INJ_CTRL_GAP_SIZE(1) | | |
612 | + QS_INJ_CTRL_VLD_BYTES(length < OCELOT_BUF_CELL_SZ ? 0 : last) | | |
613 | + QS_INJ_CTRL_EOF, priv->regs[QS] + QS_INJ_CTRL(grp)); | |
614 | + | |
615 | + /* Add dummy CRC */ | |
616 | + writel(0, priv->regs[QS] + QS_INJ_WR(grp)); | |
617 | + | |
618 | + return 0; | |
619 | +} | |
620 | + | |
621 | +static int ocelot_recv(struct udevice *dev, int flags, uchar **packetp) | |
622 | +{ | |
623 | + struct ocelot_private *priv = dev_get_priv(dev); | |
624 | + u8 grp = 0; /* Send everything on CPU group 0 */ | |
625 | + u32 *rxbuf = (u32 *)net_rx_packets[0]; | |
626 | + int i, byte_cnt = 0; | |
627 | + bool eof_flag = false, pruned_flag = false, abort_flag = false; | |
628 | + | |
629 | + if (!(readl(priv->regs[QS] + QS_XTR_DATA_PRESENT) & BIT(grp))) | |
630 | + return -EAGAIN; | |
631 | + | |
632 | + /* skip IFH */ | |
633 | + for (i = 0; i < IFH_LEN; i++) | |
634 | + readl(priv->regs[QS] + QS_XTR_RD(grp)); | |
635 | + | |
636 | + while (!eof_flag) { | |
637 | + u32 val = readl(priv->regs[QS] + QS_XTR_RD(grp)); | |
638 | + | |
639 | + switch (val) { | |
640 | + case XTR_NOT_READY: | |
641 | + debug("%d NOT_READY...?\n", byte_cnt); | |
642 | + break; | |
643 | + case XTR_ABORT: | |
644 | + /* really nedeed?? not done in linux */ | |
645 | + *rxbuf = readl(priv->regs[QS] + QS_XTR_RD(grp)); | |
646 | + abort_flag = true; | |
647 | + eof_flag = true; | |
648 | + debug("XTR_ABORT\n"); | |
649 | + break; | |
650 | + case XTR_EOF_0: | |
651 | + case XTR_EOF_1: | |
652 | + case XTR_EOF_2: | |
653 | + case XTR_EOF_3: | |
654 | + byte_cnt += XTR_VALID_BYTES(val); | |
655 | + *rxbuf = readl(priv->regs[QS] + QS_XTR_RD(grp)); | |
656 | + eof_flag = true; | |
657 | + debug("EOF\n"); | |
658 | + break; | |
659 | + case XTR_PRUNED: | |
660 | + /* But get the last 4 bytes as well */ | |
661 | + eof_flag = true; | |
662 | + pruned_flag = true; | |
663 | + debug("PRUNED\n"); | |
664 | + /* fallthrough */ | |
665 | + case XTR_ESCAPE: | |
666 | + *rxbuf = readl(priv->regs[QS] + QS_XTR_RD(grp)); | |
667 | + byte_cnt += 4; | |
668 | + rxbuf++; | |
669 | + debug("ESCAPED\n"); | |
670 | + break; | |
671 | + default: | |
672 | + *rxbuf = val; | |
673 | + byte_cnt += 4; | |
674 | + rxbuf++; | |
675 | + } | |
676 | + } | |
677 | + | |
678 | + if (abort_flag || pruned_flag || !eof_flag) { | |
679 | + debug("Discarded frame: abort:%d pruned:%d eof:%d\n", | |
680 | + abort_flag, pruned_flag, eof_flag); | |
681 | + return -EAGAIN; | |
682 | + } | |
683 | + | |
684 | + *packetp = net_rx_packets[0]; | |
685 | + | |
686 | + return byte_cnt; | |
687 | +} | |
688 | + | |
689 | +static int ocelot_probe(struct udevice *dev) | |
690 | +{ | |
691 | + struct ocelot_private *priv = dev_get_priv(dev); | |
692 | + int ret, i; | |
693 | + | |
694 | + struct { | |
695 | + enum ocelot_target id; | |
696 | + char *name; | |
697 | + } reg[] = { | |
698 | + { SYS, "sys" }, | |
699 | + { REW, "rew" }, | |
700 | + { QSYS, "qsys" }, | |
701 | + { ANA, "ana" }, | |
702 | + { QS, "qs" }, | |
703 | + { HSIO, "hsio" }, | |
704 | + { PORT0, "port0" }, | |
705 | + { PORT1, "port1" }, | |
706 | + { PORT2, "port2" }, | |
707 | + { PORT3, "port3" }, | |
708 | + }; | |
709 | + | |
710 | + for (i = 0; i < ARRAY_SIZE(reg); i++) { | |
711 | + priv->regs[reg[i].id] = dev_remap_addr_name(dev, reg[i].name); | |
712 | + if (!priv->regs[reg[i].id]) { | |
713 | + pr_err | |
714 | + ("Error %d: can't get regs base addresses for %s\n", | |
715 | + ret, reg[i].name); | |
716 | + return -ENOMEM; | |
717 | + } | |
718 | + } | |
719 | + | |
720 | + priv->bus[INTERNAL] = ocelot_mdiobus_init(dev); | |
721 | + | |
722 | + for (i = 0; i < 4; i++) { | |
723 | + phy_connect(priv->bus[INTERNAL], i, dev, | |
724 | + PHY_INTERFACE_MODE_NONE); | |
725 | + } | |
726 | + | |
727 | + return 0; | |
728 | +} | |
729 | + | |
730 | +static int ocelot_remove(struct udevice *dev) | |
731 | +{ | |
732 | + struct ocelot_private *priv = dev_get_priv(dev); | |
733 | + int i; | |
734 | + | |
735 | + for (i = 0; i < NUM_PHY; i++) { | |
736 | + mdio_unregister(priv->bus[i]); | |
737 | + mdio_free(priv->bus[i]); | |
738 | + } | |
739 | + | |
740 | + return 0; | |
741 | +} | |
742 | + | |
743 | +static const struct eth_ops ocelot_ops = { | |
744 | + .start = ocelot_start, | |
745 | + .stop = ocelot_stop, | |
746 | + .send = ocelot_send, | |
747 | + .recv = ocelot_recv, | |
748 | + .write_hwaddr = ocelot_write_hwaddr, | |
749 | +}; | |
750 | + | |
751 | +static const struct udevice_id mscc_ocelot_ids[] = { | |
752 | + {.compatible = "mscc,vsc7514-switch"}, | |
753 | + { /* Sentinel */ } | |
754 | +}; | |
755 | + | |
756 | +U_BOOT_DRIVER(ocelot) = { | |
757 | + .name = "ocelot-switch", | |
758 | + .id = UCLASS_ETH, | |
759 | + .of_match = mscc_ocelot_ids, | |
760 | + .probe = ocelot_probe, | |
761 | + .remove = ocelot_remove, | |
762 | + .ops = &ocelot_ops, | |
763 | + .priv_auto_alloc_size = sizeof(struct ocelot_private), | |
764 | + .platdata_auto_alloc_size = sizeof(struct eth_pdata), | |
765 | +}; |