1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* |
3 | * Copyright 2021-2022 Innovative Advantage Inc. |
4 | */ |
5 | |
6 | #include <linux/mfd/ocelot.h> |
7 | #include <linux/platform_device.h> |
8 | #include <linux/regmap.h> |
9 | #include <soc/mscc/ocelot.h> |
10 | #include <soc/mscc/vsc7514_regs.h> |
11 | #include "felix.h" |
12 | |
13 | #define VSC7514_NUM_PORTS 11 |
14 | |
15 | #define OCELOT_PORT_MODE_SERDES (OCELOT_PORT_MODE_SGMII | \ |
16 | OCELOT_PORT_MODE_QSGMII) |
17 | |
18 | static const u32 vsc7512_port_modes[VSC7514_NUM_PORTS] = { |
19 | OCELOT_PORT_MODE_INTERNAL, |
20 | OCELOT_PORT_MODE_INTERNAL, |
21 | OCELOT_PORT_MODE_INTERNAL, |
22 | OCELOT_PORT_MODE_INTERNAL, |
23 | OCELOT_PORT_MODE_SERDES, |
24 | OCELOT_PORT_MODE_SERDES, |
25 | OCELOT_PORT_MODE_SERDES, |
26 | OCELOT_PORT_MODE_SERDES, |
27 | OCELOT_PORT_MODE_SERDES, |
28 | OCELOT_PORT_MODE_SGMII, |
29 | OCELOT_PORT_MODE_SERDES, |
30 | }; |
31 | |
32 | static const struct ocelot_ops ocelot_ext_ops = { |
33 | .reset = ocelot_reset, |
34 | .wm_enc = ocelot_wm_enc, |
35 | .wm_dec = ocelot_wm_dec, |
36 | .wm_stat = ocelot_wm_stat, |
37 | .port_to_netdev = felix_port_to_netdev, |
38 | .netdev_to_port = felix_netdev_to_port, |
39 | }; |
40 | |
41 | static const char * const vsc7512_resource_names[TARGET_MAX] = { |
42 | [SYS] = "sys" , |
43 | [REW] = "rew" , |
44 | [S0] = "s0" , |
45 | [S1] = "s1" , |
46 | [S2] = "s2" , |
47 | [QS] = "qs" , |
48 | [QSYS] = "qsys" , |
49 | [ANA] = "ana" , |
50 | }; |
51 | |
52 | static const struct felix_info vsc7512_info = { |
53 | .resource_names = vsc7512_resource_names, |
54 | .regfields = vsc7514_regfields, |
55 | .map = vsc7514_regmap, |
56 | .ops = &ocelot_ext_ops, |
57 | .vcap = vsc7514_vcap_props, |
58 | .num_mact_rows = 1024, |
59 | .num_ports = VSC7514_NUM_PORTS, |
60 | .num_tx_queues = OCELOT_NUM_TC, |
61 | .port_modes = vsc7512_port_modes, |
62 | .phylink_mac_config = ocelot_phylink_mac_config, |
63 | .configure_serdes = ocelot_port_configure_serdes, |
64 | }; |
65 | |
66 | static int ocelot_ext_probe(struct platform_device *pdev) |
67 | { |
68 | struct device *dev = &pdev->dev; |
69 | struct dsa_switch *ds; |
70 | struct ocelot *ocelot; |
71 | struct felix *felix; |
72 | int err; |
73 | |
74 | felix = kzalloc(size: sizeof(*felix), GFP_KERNEL); |
75 | if (!felix) |
76 | return -ENOMEM; |
77 | |
78 | dev_set_drvdata(dev, data: felix); |
79 | |
80 | ocelot = &felix->ocelot; |
81 | ocelot->dev = dev; |
82 | |
83 | ocelot->num_flooding_pgids = 1; |
84 | |
85 | felix->info = &vsc7512_info; |
86 | |
87 | ds = kzalloc(size: sizeof(*ds), GFP_KERNEL); |
88 | if (!ds) { |
89 | err = -ENOMEM; |
90 | dev_err_probe(dev, err, fmt: "Failed to allocate DSA switch\n" ); |
91 | goto err_free_felix; |
92 | } |
93 | |
94 | ds->dev = dev; |
95 | ds->num_ports = felix->info->num_ports; |
96 | ds->num_tx_queues = felix->info->num_tx_queues; |
97 | |
98 | ds->ops = &felix_switch_ops; |
99 | ds->priv = ocelot; |
100 | felix->ds = ds; |
101 | felix->tag_proto = DSA_TAG_PROTO_OCELOT; |
102 | |
103 | err = dsa_register_switch(ds); |
104 | if (err) { |
105 | dev_err_probe(dev, err, fmt: "Failed to register DSA switch\n" ); |
106 | goto err_free_ds; |
107 | } |
108 | |
109 | return 0; |
110 | |
111 | err_free_ds: |
112 | kfree(objp: ds); |
113 | err_free_felix: |
114 | kfree(objp: felix); |
115 | return err; |
116 | } |
117 | |
118 | static void ocelot_ext_remove(struct platform_device *pdev) |
119 | { |
120 | struct felix *felix = dev_get_drvdata(dev: &pdev->dev); |
121 | |
122 | if (!felix) |
123 | return; |
124 | |
125 | dsa_unregister_switch(ds: felix->ds); |
126 | |
127 | kfree(objp: felix->ds); |
128 | kfree(objp: felix); |
129 | } |
130 | |
131 | static void ocelot_ext_shutdown(struct platform_device *pdev) |
132 | { |
133 | struct felix *felix = dev_get_drvdata(dev: &pdev->dev); |
134 | |
135 | if (!felix) |
136 | return; |
137 | |
138 | dsa_switch_shutdown(ds: felix->ds); |
139 | |
140 | dev_set_drvdata(dev: &pdev->dev, NULL); |
141 | } |
142 | |
143 | static const struct of_device_id ocelot_ext_switch_of_match[] = { |
144 | { .compatible = "mscc,vsc7512-switch" }, |
145 | { }, |
146 | }; |
147 | MODULE_DEVICE_TABLE(of, ocelot_ext_switch_of_match); |
148 | |
149 | static struct platform_driver ocelot_ext_switch_driver = { |
150 | .driver = { |
151 | .name = "ocelot-ext-switch" , |
152 | .of_match_table = ocelot_ext_switch_of_match, |
153 | }, |
154 | .probe = ocelot_ext_probe, |
155 | .remove_new = ocelot_ext_remove, |
156 | .shutdown = ocelot_ext_shutdown, |
157 | }; |
158 | module_platform_driver(ocelot_ext_switch_driver); |
159 | |
160 | MODULE_DESCRIPTION("External Ocelot Switch driver" ); |
161 | MODULE_LICENSE("GPL" ); |
162 | MODULE_IMPORT_NS(MFD_OCELOT); |
163 | |