Skip to content

Commit db8bcaa

Browse files
HoratiuVulturdavem330
authored andcommitted
net: lan966x: add the basic lan966x driver
This patch adds basic SwitchDev driver framework for lan966x. It includes only the IO range mapping and probing of the switch. Signed-off-by: Horatiu Vultur <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 642fcf5 commit db8bcaa

File tree

7 files changed

+1207
-0
lines changed

7 files changed

+1207
-0
lines changed

drivers/net/ethernet/microchip/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ config LAN743X
5555
To compile this driver as a module, choose M here. The module will be
5656
called lan743x.
5757

58+
source "drivers/net/ethernet/microchip/lan966x/Kconfig"
5859
source "drivers/net/ethernet/microchip/sparx5/Kconfig"
5960

6061
endif # NET_VENDOR_MICROCHIP

drivers/net/ethernet/microchip/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ obj-$(CONFIG_LAN743X) += lan743x.o
99

1010
lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o
1111

12+
obj-$(CONFIG_LAN966X_SWITCH) += lan966x/
1213
obj-$(CONFIG_SPARX5_SWITCH) += sparx5/
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
config LAN966X_SWITCH
2+
tristate "Lan966x switch driver"
3+
depends on HAS_IOMEM
4+
depends on OF
5+
select PHYLINK
6+
help
7+
This driver supports the Lan966x network switch device.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
#
3+
# Makefile for the Microchip Lan966x network device drivers.
4+
#
5+
6+
obj-$(CONFIG_LAN966X_SWITCH) += lan966x-switch.o
7+
8+
lan966x-switch-objs := lan966x_main.o
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
3+
#include <linux/module.h>
4+
#include <linux/if_bridge.h>
5+
#include <linux/iopoll.h>
6+
#include <linux/of_platform.h>
7+
#include <linux/of_net.h>
8+
#include <linux/reset.h>
9+
10+
#include "lan966x_main.h"
11+
12+
#define READL_SLEEP_US 10
13+
#define READL_TIMEOUT_US 100000000
14+
15+
#define IO_RANGES 2
16+
17+
static const struct of_device_id lan966x_match[] = {
18+
{ .compatible = "microchip,lan966x-switch" },
19+
{ }
20+
};
21+
MODULE_DEVICE_TABLE(of, lan966x_match);
22+
23+
struct lan966x_main_io_resource {
24+
enum lan966x_target id;
25+
phys_addr_t offset;
26+
int range;
27+
};
28+
29+
static const struct lan966x_main_io_resource lan966x_main_iomap[] = {
30+
{ TARGET_CPU, 0xc0000, 0 }, /* 0xe00c0000 */
31+
{ TARGET_ORG, 0, 1 }, /* 0xe2000000 */
32+
{ TARGET_GCB, 0x4000, 1 }, /* 0xe2004000 */
33+
{ TARGET_QS, 0x8000, 1 }, /* 0xe2008000 */
34+
{ TARGET_CHIP_TOP, 0x10000, 1 }, /* 0xe2010000 */
35+
{ TARGET_REW, 0x14000, 1 }, /* 0xe2014000 */
36+
{ TARGET_SYS, 0x28000, 1 }, /* 0xe2028000 */
37+
{ TARGET_DEV, 0x34000, 1 }, /* 0xe2034000 */
38+
{ TARGET_DEV + 1, 0x38000, 1 }, /* 0xe2038000 */
39+
{ TARGET_DEV + 2, 0x3c000, 1 }, /* 0xe203c000 */
40+
{ TARGET_DEV + 3, 0x40000, 1 }, /* 0xe2040000 */
41+
{ TARGET_DEV + 4, 0x44000, 1 }, /* 0xe2044000 */
42+
{ TARGET_DEV + 5, 0x48000, 1 }, /* 0xe2048000 */
43+
{ TARGET_DEV + 6, 0x4c000, 1 }, /* 0xe204c000 */
44+
{ TARGET_DEV + 7, 0x50000, 1 }, /* 0xe2050000 */
45+
{ TARGET_QSYS, 0x100000, 1 }, /* 0xe2100000 */
46+
{ TARGET_AFI, 0x120000, 1 }, /* 0xe2120000 */
47+
{ TARGET_ANA, 0x140000, 1 }, /* 0xe2140000 */
48+
};
49+
50+
static int lan966x_create_targets(struct platform_device *pdev,
51+
struct lan966x *lan966x)
52+
{
53+
struct resource *iores[IO_RANGES];
54+
void __iomem *begin[IO_RANGES];
55+
int idx;
56+
57+
/* Initially map the entire range and after that update each target to
58+
* point inside the region at the correct offset. It is possible that
59+
* other devices access the same region so don't add any checks about
60+
* this.
61+
*/
62+
for (idx = 0; idx < IO_RANGES; idx++) {
63+
iores[idx] = platform_get_resource(pdev, IORESOURCE_MEM,
64+
idx);
65+
if (!iores[idx]) {
66+
dev_err(&pdev->dev, "Invalid resource\n");
67+
return -EINVAL;
68+
}
69+
70+
begin[idx] = devm_ioremap(&pdev->dev,
71+
iores[idx]->start,
72+
resource_size(iores[idx]));
73+
if (IS_ERR(begin[idx])) {
74+
dev_err(&pdev->dev, "Unable to get registers: %s\n",
75+
iores[idx]->name);
76+
return PTR_ERR(begin[idx]);
77+
}
78+
}
79+
80+
for (idx = 0; idx < ARRAY_SIZE(lan966x_main_iomap); idx++) {
81+
const struct lan966x_main_io_resource *iomap =
82+
&lan966x_main_iomap[idx];
83+
84+
lan966x->regs[iomap->id] = begin[iomap->range] + iomap->offset;
85+
}
86+
87+
return 0;
88+
}
89+
90+
static int lan966x_probe_port(struct lan966x *lan966x, u32 p,
91+
phy_interface_t phy_mode)
92+
{
93+
struct lan966x_port *port;
94+
95+
if (p >= lan966x->num_phys_ports)
96+
return -EINVAL;
97+
98+
port = devm_kzalloc(lan966x->dev, sizeof(*port), GFP_KERNEL);
99+
if (!port)
100+
return -ENOMEM;
101+
102+
port->lan966x = lan966x;
103+
port->chip_port = p;
104+
port->pvid = PORT_PVID;
105+
lan966x->ports[p] = port;
106+
107+
return 0;
108+
}
109+
110+
static void lan966x_init(struct lan966x *lan966x)
111+
{
112+
u32 p, i;
113+
114+
/* Flush queues */
115+
lan_wr(lan_rd(lan966x, QS_XTR_FLUSH) |
116+
GENMASK(1, 0),
117+
lan966x, QS_XTR_FLUSH);
118+
119+
/* Allow to drain */
120+
mdelay(1);
121+
122+
/* All Queues normal */
123+
lan_wr(lan_rd(lan966x, QS_XTR_FLUSH) &
124+
~(GENMASK(1, 0)),
125+
lan966x, QS_XTR_FLUSH);
126+
127+
/* Set MAC age time to default value, the entry is aged after
128+
* 2 * AGE_PERIOD
129+
*/
130+
lan_wr(ANA_AUTOAGE_AGE_PERIOD_SET(BR_DEFAULT_AGEING_TIME / 2 / HZ),
131+
lan966x, ANA_AUTOAGE);
132+
133+
/* Disable learning for frames discarded by VLAN ingress filtering */
134+
lan_rmw(ANA_ADVLEARN_VLAN_CHK_SET(1),
135+
ANA_ADVLEARN_VLAN_CHK,
136+
lan966x, ANA_ADVLEARN);
137+
138+
/* Setup frame ageing - "2 sec" - The unit is 6.5 us on lan966x */
139+
lan_wr(SYS_FRM_AGING_AGE_TX_ENA_SET(1) |
140+
(20000000 / 65),
141+
lan966x, SYS_FRM_AGING);
142+
143+
/* Map the 8 CPU extraction queues to CPU port */
144+
lan_wr(0, lan966x, QSYS_CPU_GROUP_MAP);
145+
146+
/* Do byte-swap and expect status after last data word
147+
* Extraction: Mode: manual extraction) | Byte_swap
148+
*/
149+
lan_wr(QS_XTR_GRP_CFG_MODE_SET(1) |
150+
QS_XTR_GRP_CFG_BYTE_SWAP_SET(1),
151+
lan966x, QS_XTR_GRP_CFG(0));
152+
153+
/* Injection: Mode: manual injection | Byte_swap */
154+
lan_wr(QS_INJ_GRP_CFG_MODE_SET(1) |
155+
QS_INJ_GRP_CFG_BYTE_SWAP_SET(1),
156+
lan966x, QS_INJ_GRP_CFG(0));
157+
158+
lan_rmw(QS_INJ_CTRL_GAP_SIZE_SET(0),
159+
QS_INJ_CTRL_GAP_SIZE,
160+
lan966x, QS_INJ_CTRL(0));
161+
162+
/* Enable IFH insertion/parsing on CPU ports */
163+
lan_wr(SYS_PORT_MODE_INCL_INJ_HDR_SET(1) |
164+
SYS_PORT_MODE_INCL_XTR_HDR_SET(1),
165+
lan966x, SYS_PORT_MODE(CPU_PORT));
166+
167+
/* Setup flooding PGIDs */
168+
lan_wr(ANA_FLOODING_IPMC_FLD_MC4_DATA_SET(PGID_MCIPV4) |
169+
ANA_FLOODING_IPMC_FLD_MC4_CTRL_SET(PGID_MC) |
170+
ANA_FLOODING_IPMC_FLD_MC6_DATA_SET(PGID_MC) |
171+
ANA_FLOODING_IPMC_FLD_MC6_CTRL_SET(PGID_MC),
172+
lan966x, ANA_FLOODING_IPMC);
173+
174+
/* There are 8 priorities */
175+
for (i = 0; i < 8; ++i)
176+
lan_rmw(ANA_FLOODING_FLD_MULTICAST_SET(PGID_MC) |
177+
ANA_FLOODING_FLD_BROADCAST_SET(PGID_BC),
178+
ANA_FLOODING_FLD_MULTICAST |
179+
ANA_FLOODING_FLD_BROADCAST,
180+
lan966x, ANA_FLOODING(i));
181+
182+
for (i = 0; i < PGID_ENTRIES; ++i)
183+
/* Set all the entries to obey VLAN_VLAN */
184+
lan_rmw(ANA_PGID_CFG_OBEY_VLAN_SET(1),
185+
ANA_PGID_CFG_OBEY_VLAN,
186+
lan966x, ANA_PGID_CFG(i));
187+
188+
for (p = 0; p < lan966x->num_phys_ports; p++) {
189+
/* Disable bridging by default */
190+
lan_rmw(ANA_PGID_PGID_SET(0x0),
191+
ANA_PGID_PGID,
192+
lan966x, ANA_PGID(p + PGID_SRC));
193+
194+
/* Do not forward BPDU frames to the front ports and copy them
195+
* to CPU
196+
*/
197+
lan_wr(0xffff, lan966x, ANA_CPU_FWD_BPDU_CFG(p));
198+
}
199+
200+
/* Set source buffer size for each priority and each port to 1500 bytes */
201+
for (i = 0; i <= QSYS_Q_RSRV; ++i) {
202+
lan_wr(1500 / 64, lan966x, QSYS_RES_CFG(i));
203+
lan_wr(1500 / 64, lan966x, QSYS_RES_CFG(512 + i));
204+
}
205+
206+
/* Enable switching to/from cpu port */
207+
lan_wr(QSYS_SW_PORT_MODE_PORT_ENA_SET(1) |
208+
QSYS_SW_PORT_MODE_SCH_NEXT_CFG_SET(1) |
209+
QSYS_SW_PORT_MODE_INGRESS_DROP_MODE_SET(1),
210+
lan966x, QSYS_SW_PORT_MODE(CPU_PORT));
211+
212+
/* Configure and enable the CPU port */
213+
lan_rmw(ANA_PGID_PGID_SET(0),
214+
ANA_PGID_PGID,
215+
lan966x, ANA_PGID(CPU_PORT));
216+
lan_rmw(ANA_PGID_PGID_SET(BIT(CPU_PORT)),
217+
ANA_PGID_PGID,
218+
lan966x, ANA_PGID(PGID_CPU));
219+
220+
/* Multicast to all other ports */
221+
lan_rmw(GENMASK(lan966x->num_phys_ports - 1, 0),
222+
ANA_PGID_PGID,
223+
lan966x, ANA_PGID(PGID_MC));
224+
225+
/* This will be controlled by mrouter ports */
226+
lan_rmw(GENMASK(lan966x->num_phys_ports - 1, 0),
227+
ANA_PGID_PGID,
228+
lan966x, ANA_PGID(PGID_MCIPV4));
229+
230+
/* Broadcast to the CPU port and to other ports */
231+
lan_rmw(ANA_PGID_PGID_SET(BIT(CPU_PORT) | GENMASK(lan966x->num_phys_ports - 1, 0)),
232+
ANA_PGID_PGID,
233+
lan966x, ANA_PGID(PGID_BC));
234+
235+
lan_wr(REW_PORT_CFG_NO_REWRITE_SET(1),
236+
lan966x, REW_PORT_CFG(CPU_PORT));
237+
238+
lan_rmw(ANA_ANAINTR_INTR_ENA_SET(1),
239+
ANA_ANAINTR_INTR_ENA,
240+
lan966x, ANA_ANAINTR);
241+
}
242+
243+
static int lan966x_ram_init(struct lan966x *lan966x)
244+
{
245+
return lan_rd(lan966x, SYS_RAM_INIT);
246+
}
247+
248+
static int lan966x_reset_switch(struct lan966x *lan966x)
249+
{
250+
struct reset_control *switch_reset, *phy_reset;
251+
int val = 0;
252+
int ret;
253+
254+
switch_reset = devm_reset_control_get_shared(lan966x->dev, "switch");
255+
if (IS_ERR(switch_reset))
256+
return dev_err_probe(lan966x->dev, PTR_ERR(switch_reset),
257+
"Could not obtain switch reset");
258+
259+
phy_reset = devm_reset_control_get_shared(lan966x->dev, "phy");
260+
if (IS_ERR(phy_reset))
261+
return dev_err_probe(lan966x->dev, PTR_ERR(phy_reset),
262+
"Could not obtain phy reset\n");
263+
264+
reset_control_reset(switch_reset);
265+
reset_control_reset(phy_reset);
266+
267+
lan_wr(SYS_RESET_CFG_CORE_ENA_SET(0), lan966x, SYS_RESET_CFG);
268+
lan_wr(SYS_RAM_INIT_RAM_INIT_SET(1), lan966x, SYS_RAM_INIT);
269+
ret = readx_poll_timeout(lan966x_ram_init, lan966x,
270+
val, (val & BIT(1)) == 0, READL_SLEEP_US,
271+
READL_TIMEOUT_US);
272+
if (ret)
273+
return ret;
274+
275+
lan_wr(SYS_RESET_CFG_CORE_ENA_SET(1), lan966x, SYS_RESET_CFG);
276+
277+
return 0;
278+
}
279+
280+
static int lan966x_probe(struct platform_device *pdev)
281+
{
282+
struct fwnode_handle *ports, *portnp;
283+
struct lan966x *lan966x;
284+
int err, i;
285+
286+
lan966x = devm_kzalloc(&pdev->dev, sizeof(*lan966x), GFP_KERNEL);
287+
if (!lan966x)
288+
return -ENOMEM;
289+
290+
platform_set_drvdata(pdev, lan966x);
291+
lan966x->dev = &pdev->dev;
292+
293+
ports = device_get_named_child_node(&pdev->dev, "ethernet-ports");
294+
if (!ports)
295+
return dev_err_probe(&pdev->dev, -ENODEV,
296+
"no ethernet-ports child found\n");
297+
298+
err = lan966x_create_targets(pdev, lan966x);
299+
if (err)
300+
return dev_err_probe(&pdev->dev, err,
301+
"Failed to create targets");
302+
303+
err = lan966x_reset_switch(lan966x);
304+
if (err)
305+
return dev_err_probe(&pdev->dev, err, "Reset failed");
306+
307+
i = 0;
308+
fwnode_for_each_available_child_node(ports, portnp)
309+
++i;
310+
311+
lan966x->num_phys_ports = i;
312+
lan966x->ports = devm_kcalloc(&pdev->dev, lan966x->num_phys_ports,
313+
sizeof(struct lan966x_port *),
314+
GFP_KERNEL);
315+
if (!lan966x->ports)
316+
return -ENOMEM;
317+
318+
/* There QS system has 32KB of memory */
319+
lan966x->shared_queue_sz = LAN966X_BUFFER_MEMORY;
320+
321+
/* init switch */
322+
lan966x_init(lan966x);
323+
324+
/* go over the child nodes */
325+
fwnode_for_each_available_child_node(ports, portnp) {
326+
phy_interface_t phy_mode;
327+
u32 p;
328+
329+
if (fwnode_property_read_u32(portnp, "reg", &p))
330+
continue;
331+
332+
phy_mode = fwnode_get_phy_mode(portnp);
333+
err = lan966x_probe_port(lan966x, p, phy_mode);
334+
if (err)
335+
goto cleanup_ports;
336+
}
337+
338+
return 0;
339+
340+
cleanup_ports:
341+
fwnode_handle_put(portnp);
342+
343+
return err;
344+
}
345+
346+
static struct platform_driver lan966x_driver = {
347+
.probe = lan966x_probe,
348+
.driver = {
349+
.name = "lan966x-switch",
350+
.of_match_table = lan966x_match,
351+
},
352+
};
353+
module_platform_driver(lan966x_driver);
354+
355+
MODULE_DESCRIPTION("Microchip LAN966X switch driver");
356+
MODULE_AUTHOR("Horatiu Vultur <[email protected]>");
357+
MODULE_LICENSE("Dual MIT/GPL");

0 commit comments

Comments
 (0)