]>
Commit | Line | Data |
---|---|---|
56051948 VO |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright 2019 NXP Semiconductors | |
3 | */ | |
4 | #include <uapi/linux/if_bridge.h> | |
5 | #include <soc/mscc/ocelot.h> | |
6 | #include <linux/module.h> | |
7 | #include <linux/pci.h> | |
8 | #include <linux/of.h> | |
9 | #include <net/dsa.h> | |
10 | #include "felix.h" | |
11 | ||
12 | static enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds, | |
13 | int port) | |
14 | { | |
15 | return DSA_TAG_PROTO_OCELOT; | |
16 | } | |
17 | ||
18 | static int felix_set_ageing_time(struct dsa_switch *ds, | |
19 | unsigned int ageing_time) | |
20 | { | |
21 | struct ocelot *ocelot = ds->priv; | |
22 | ||
23 | ocelot_set_ageing_time(ocelot, ageing_time); | |
24 | ||
25 | return 0; | |
26 | } | |
27 | ||
28 | static void felix_adjust_link(struct dsa_switch *ds, int port, | |
29 | struct phy_device *phydev) | |
30 | { | |
31 | struct ocelot *ocelot = ds->priv; | |
32 | ||
33 | ocelot_adjust_link(ocelot, port, phydev); | |
34 | } | |
35 | ||
36 | static int felix_fdb_dump(struct dsa_switch *ds, int port, | |
37 | dsa_fdb_dump_cb_t *cb, void *data) | |
38 | { | |
39 | struct ocelot *ocelot = ds->priv; | |
40 | ||
41 | return ocelot_fdb_dump(ocelot, port, cb, data); | |
42 | } | |
43 | ||
44 | static int felix_fdb_add(struct dsa_switch *ds, int port, | |
45 | const unsigned char *addr, u16 vid) | |
46 | { | |
47 | struct ocelot *ocelot = ds->priv; | |
48 | bool vlan_aware; | |
49 | ||
50 | vlan_aware = dsa_port_is_vlan_filtering(dsa_to_port(ds, port)); | |
51 | ||
52 | return ocelot_fdb_add(ocelot, port, addr, vid, vlan_aware); | |
53 | } | |
54 | ||
55 | static int felix_fdb_del(struct dsa_switch *ds, int port, | |
56 | const unsigned char *addr, u16 vid) | |
57 | { | |
58 | struct ocelot *ocelot = ds->priv; | |
59 | ||
60 | return ocelot_fdb_del(ocelot, port, addr, vid); | |
61 | } | |
62 | ||
63 | static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port, | |
64 | u8 state) | |
65 | { | |
66 | struct ocelot *ocelot = ds->priv; | |
67 | ||
68 | return ocelot_bridge_stp_state_set(ocelot, port, state); | |
69 | } | |
70 | ||
71 | static int felix_bridge_join(struct dsa_switch *ds, int port, | |
72 | struct net_device *br) | |
73 | { | |
74 | struct ocelot *ocelot = ds->priv; | |
75 | ||
76 | return ocelot_port_bridge_join(ocelot, port, br); | |
77 | } | |
78 | ||
79 | static void felix_bridge_leave(struct dsa_switch *ds, int port, | |
80 | struct net_device *br) | |
81 | { | |
82 | struct ocelot *ocelot = ds->priv; | |
83 | ||
84 | ocelot_port_bridge_leave(ocelot, port, br); | |
85 | } | |
86 | ||
87 | /* This callback needs to be present */ | |
88 | static int felix_vlan_prepare(struct dsa_switch *ds, int port, | |
89 | const struct switchdev_obj_port_vlan *vlan) | |
90 | { | |
91 | return 0; | |
92 | } | |
93 | ||
94 | static int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) | |
95 | { | |
96 | struct ocelot *ocelot = ds->priv; | |
97 | ||
98 | ocelot_port_vlan_filtering(ocelot, port, enabled); | |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
103 | static void felix_vlan_add(struct dsa_switch *ds, int port, | |
104 | const struct switchdev_obj_port_vlan *vlan) | |
105 | { | |
106 | struct ocelot *ocelot = ds->priv; | |
107 | u16 vid; | |
108 | int err; | |
109 | ||
110 | for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { | |
111 | err = ocelot_vlan_add(ocelot, port, vid, | |
112 | vlan->flags & BRIDGE_VLAN_INFO_PVID, | |
113 | vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); | |
114 | if (err) { | |
115 | dev_err(ds->dev, "Failed to add VLAN %d to port %d: %d\n", | |
116 | vid, port, err); | |
117 | return; | |
118 | } | |
119 | } | |
120 | } | |
121 | ||
122 | static int felix_vlan_del(struct dsa_switch *ds, int port, | |
123 | const struct switchdev_obj_port_vlan *vlan) | |
124 | { | |
125 | struct ocelot *ocelot = ds->priv; | |
126 | u16 vid; | |
127 | int err; | |
128 | ||
129 | for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { | |
130 | err = ocelot_vlan_del(ocelot, port, vid); | |
131 | if (err) { | |
132 | dev_err(ds->dev, "Failed to remove VLAN %d from port %d: %d\n", | |
133 | vid, port, err); | |
134 | return err; | |
135 | } | |
136 | } | |
137 | return 0; | |
138 | } | |
139 | ||
140 | static int felix_port_enable(struct dsa_switch *ds, int port, | |
141 | struct phy_device *phy) | |
142 | { | |
143 | struct ocelot *ocelot = ds->priv; | |
144 | ||
145 | ocelot_port_enable(ocelot, port, phy); | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | static void felix_port_disable(struct dsa_switch *ds, int port) | |
151 | { | |
152 | struct ocelot *ocelot = ds->priv; | |
153 | ||
154 | return ocelot_port_disable(ocelot, port); | |
155 | } | |
156 | ||
157 | static void felix_get_strings(struct dsa_switch *ds, int port, | |
158 | u32 stringset, u8 *data) | |
159 | { | |
160 | struct ocelot *ocelot = ds->priv; | |
161 | ||
162 | return ocelot_get_strings(ocelot, port, stringset, data); | |
163 | } | |
164 | ||
165 | static void felix_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) | |
166 | { | |
167 | struct ocelot *ocelot = ds->priv; | |
168 | ||
169 | ocelot_get_ethtool_stats(ocelot, port, data); | |
170 | } | |
171 | ||
172 | static int felix_get_sset_count(struct dsa_switch *ds, int port, int sset) | |
173 | { | |
174 | struct ocelot *ocelot = ds->priv; | |
175 | ||
176 | return ocelot_get_sset_count(ocelot, port, sset); | |
177 | } | |
178 | ||
179 | static int felix_get_ts_info(struct dsa_switch *ds, int port, | |
180 | struct ethtool_ts_info *info) | |
181 | { | |
182 | struct ocelot *ocelot = ds->priv; | |
183 | ||
184 | return ocelot_get_ts_info(ocelot, port, info); | |
185 | } | |
186 | ||
187 | static int felix_init_structs(struct felix *felix, int num_phys_ports) | |
188 | { | |
189 | struct ocelot *ocelot = &felix->ocelot; | |
190 | resource_size_t base; | |
191 | int port, i, err; | |
192 | ||
193 | ocelot->num_phys_ports = num_phys_ports; | |
194 | ocelot->ports = devm_kcalloc(ocelot->dev, num_phys_ports, | |
195 | sizeof(struct ocelot_port *), GFP_KERNEL); | |
196 | if (!ocelot->ports) | |
197 | return -ENOMEM; | |
198 | ||
199 | ocelot->map = felix->info->map; | |
200 | ocelot->stats_layout = felix->info->stats_layout; | |
201 | ocelot->num_stats = felix->info->num_stats; | |
202 | ocelot->shared_queue_sz = felix->info->shared_queue_sz; | |
203 | ocelot->ops = felix->info->ops; | |
204 | ||
205 | base = pci_resource_start(felix->pdev, felix->info->pci_bar); | |
206 | ||
207 | for (i = 0; i < TARGET_MAX; i++) { | |
208 | struct regmap *target; | |
209 | struct resource *res; | |
210 | ||
211 | if (!felix->info->target_io_res[i].name) | |
212 | continue; | |
213 | ||
214 | res = &felix->info->target_io_res[i]; | |
215 | res->flags = IORESOURCE_MEM; | |
216 | res->start += base; | |
217 | res->end += base; | |
218 | ||
219 | target = ocelot_regmap_init(ocelot, res); | |
220 | if (IS_ERR(target)) { | |
221 | dev_err(ocelot->dev, | |
222 | "Failed to map device memory space\n"); | |
223 | return PTR_ERR(target); | |
224 | } | |
225 | ||
226 | ocelot->targets[i] = target; | |
227 | } | |
228 | ||
229 | err = ocelot_regfields_init(ocelot, felix->info->regfields); | |
230 | if (err) { | |
231 | dev_err(ocelot->dev, "failed to init reg fields map\n"); | |
232 | return err; | |
233 | } | |
234 | ||
235 | for (port = 0; port < num_phys_ports; port++) { | |
236 | struct ocelot_port *ocelot_port; | |
237 | void __iomem *port_regs; | |
238 | struct resource *res; | |
239 | ||
240 | ocelot_port = devm_kzalloc(ocelot->dev, | |
241 | sizeof(struct ocelot_port), | |
242 | GFP_KERNEL); | |
243 | if (!ocelot_port) { | |
244 | dev_err(ocelot->dev, | |
245 | "failed to allocate port memory\n"); | |
246 | return -ENOMEM; | |
247 | } | |
248 | ||
249 | res = &felix->info->port_io_res[port]; | |
250 | res->flags = IORESOURCE_MEM; | |
251 | res->start += base; | |
252 | res->end += base; | |
253 | ||
254 | port_regs = devm_ioremap_resource(ocelot->dev, res); | |
255 | if (IS_ERR(port_regs)) { | |
256 | dev_err(ocelot->dev, | |
257 | "failed to map registers for port %d\n", port); | |
258 | return PTR_ERR(port_regs); | |
259 | } | |
260 | ||
261 | ocelot_port->ocelot = ocelot; | |
262 | ocelot_port->regs = port_regs; | |
263 | ocelot->ports[port] = ocelot_port; | |
264 | } | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
269 | /* Hardware initialization done here so that we can allocate structures with | |
270 | * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing | |
271 | * us to allocate structures twice (leak memory) and map PCI memory twice | |
272 | * (which will not work). | |
273 | */ | |
274 | static int felix_setup(struct dsa_switch *ds) | |
275 | { | |
276 | struct ocelot *ocelot = ds->priv; | |
277 | struct felix *felix = ocelot_to_felix(ocelot); | |
278 | int port, err; | |
279 | ||
280 | err = felix_init_structs(felix, ds->num_ports); | |
281 | if (err) | |
282 | return err; | |
283 | ||
284 | ocelot_init(ocelot); | |
285 | ||
286 | for (port = 0; port < ds->num_ports; port++) { | |
287 | ocelot_init_port(ocelot, port); | |
288 | ||
289 | if (port == dsa_upstream_port(ds, port)) | |
290 | ocelot_set_cpu_port(ocelot, port, | |
291 | OCELOT_TAG_PREFIX_NONE, | |
292 | OCELOT_TAG_PREFIX_LONG); | |
293 | } | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | static void felix_teardown(struct dsa_switch *ds) | |
299 | { | |
300 | struct ocelot *ocelot = ds->priv; | |
301 | ||
302 | /* stop workqueue thread */ | |
303 | ocelot_deinit(ocelot); | |
304 | } | |
305 | ||
306 | static const struct dsa_switch_ops felix_switch_ops = { | |
307 | .get_tag_protocol = felix_get_tag_protocol, | |
308 | .setup = felix_setup, | |
309 | .teardown = felix_teardown, | |
310 | .set_ageing_time = felix_set_ageing_time, | |
311 | .get_strings = felix_get_strings, | |
312 | .get_ethtool_stats = felix_get_ethtool_stats, | |
313 | .get_sset_count = felix_get_sset_count, | |
314 | .get_ts_info = felix_get_ts_info, | |
315 | .adjust_link = felix_adjust_link, | |
316 | .port_enable = felix_port_enable, | |
317 | .port_disable = felix_port_disable, | |
318 | .port_fdb_dump = felix_fdb_dump, | |
319 | .port_fdb_add = felix_fdb_add, | |
320 | .port_fdb_del = felix_fdb_del, | |
321 | .port_bridge_join = felix_bridge_join, | |
322 | .port_bridge_leave = felix_bridge_leave, | |
323 | .port_stp_state_set = felix_bridge_stp_state_set, | |
324 | .port_vlan_prepare = felix_vlan_prepare, | |
325 | .port_vlan_filtering = felix_vlan_filtering, | |
326 | .port_vlan_add = felix_vlan_add, | |
327 | .port_vlan_del = felix_vlan_del, | |
328 | }; | |
329 | ||
330 | static struct felix_info *felix_instance_tbl[] = { | |
331 | [FELIX_INSTANCE_VSC9959] = &felix_info_vsc9959, | |
332 | }; | |
333 | ||
334 | static int felix_pci_probe(struct pci_dev *pdev, | |
335 | const struct pci_device_id *id) | |
336 | { | |
337 | enum felix_instance instance = id->driver_data; | |
338 | struct dsa_switch *ds; | |
339 | struct ocelot *ocelot; | |
340 | struct felix *felix; | |
341 | int err; | |
342 | ||
343 | err = pci_enable_device(pdev); | |
344 | if (err) { | |
345 | dev_err(&pdev->dev, "device enable failed\n"); | |
346 | goto err_pci_enable; | |
347 | } | |
348 | ||
349 | /* set up for high or low dma */ | |
350 | err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); | |
351 | if (err) { | |
352 | err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); | |
353 | if (err) { | |
354 | dev_err(&pdev->dev, | |
355 | "DMA configuration failed: 0x%x\n", err); | |
356 | goto err_dma; | |
357 | } | |
358 | } | |
359 | ||
360 | felix = kzalloc(sizeof(struct felix), GFP_KERNEL); | |
361 | if (!felix) { | |
362 | err = -ENOMEM; | |
363 | dev_err(&pdev->dev, "Failed to allocate driver memory\n"); | |
364 | goto err_alloc_felix; | |
365 | } | |
366 | ||
367 | pci_set_drvdata(pdev, felix); | |
368 | ocelot = &felix->ocelot; | |
369 | ocelot->dev = &pdev->dev; | |
370 | felix->pdev = pdev; | |
371 | felix->info = felix_instance_tbl[instance]; | |
372 | ||
373 | pci_set_master(pdev); | |
374 | ||
375 | ds = kzalloc(sizeof(struct dsa_switch), GFP_KERNEL); | |
376 | if (!ds) { | |
377 | err = -ENOMEM; | |
378 | dev_err(&pdev->dev, "Failed to allocate DSA switch\n"); | |
379 | goto err_alloc_ds; | |
380 | } | |
381 | ||
382 | ds->dev = &pdev->dev; | |
383 | ds->num_ports = felix->info->num_ports; | |
384 | ds->ops = &felix_switch_ops; | |
385 | ds->priv = ocelot; | |
386 | felix->ds = ds; | |
387 | ||
388 | err = dsa_register_switch(ds); | |
389 | if (err) { | |
390 | dev_err(&pdev->dev, "Failed to register DSA switch: %d\n", err); | |
391 | goto err_register_ds; | |
392 | } | |
393 | ||
394 | return 0; | |
395 | ||
396 | err_register_ds: | |
397 | kfree(ds); | |
398 | err_alloc_ds: | |
399 | err_alloc_felix: | |
400 | kfree(felix); | |
401 | err_dma: | |
402 | pci_disable_device(pdev); | |
403 | err_pci_enable: | |
404 | return err; | |
405 | } | |
406 | ||
407 | static void felix_pci_remove(struct pci_dev *pdev) | |
408 | { | |
409 | struct felix *felix; | |
410 | ||
411 | felix = pci_get_drvdata(pdev); | |
412 | ||
413 | dsa_unregister_switch(felix->ds); | |
414 | ||
415 | kfree(felix->ds); | |
416 | kfree(felix); | |
417 | ||
418 | pci_disable_device(pdev); | |
419 | } | |
420 | ||
421 | static struct pci_device_id felix_ids[] = { | |
422 | { | |
423 | /* NXP LS1028A */ | |
424 | PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0xEEF0), | |
425 | .driver_data = FELIX_INSTANCE_VSC9959, | |
426 | }, | |
427 | { 0, } | |
428 | }; | |
429 | MODULE_DEVICE_TABLE(pci, felix_ids); | |
430 | ||
431 | static struct pci_driver felix_pci_driver = { | |
432 | .name = KBUILD_MODNAME, | |
433 | .id_table = felix_ids, | |
434 | .probe = felix_pci_probe, | |
435 | .remove = felix_pci_remove, | |
436 | }; | |
437 | ||
438 | module_pci_driver(felix_pci_driver); | |
439 | ||
440 | MODULE_DESCRIPTION("Felix Switch driver"); | |
441 | MODULE_LICENSE("GPL v2"); |