kirkwood: switch to 3.14
[openwrt.git] / target / linux / kirkwood / patches-3.10 / 0025-net-mv643xx_eth-add-DT-parsing-support.patch
1 From 0e701e28611fc08558406accadae40be36fe5289 Mon Sep 17 00:00:00 2001
2 From: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
3 Date: Wed, 29 May 2013 09:32:48 +0000
4 Subject: [PATCH 25/29] net: mv643xx_eth: add DT parsing support
5
6 This adds device tree parsing support for the shared driver of mv643xx_eth.
7 As the bindings are slightly different from current PPC bindings new binding
8 documentation is also added. Following PPC-style device setup, the shared
9 driver now also adds port platform_devices and sets up port platform_data.
10
11 Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
12 Signed-off-by: David S. Miller <davem@davemloft.net>
13 ---
14  .../devicetree/bindings/net/marvell-orion-net.txt  |  85 ++++++++++++
15  drivers/net/ethernet/marvell/mv643xx_eth.c         | 153 ++++++++++++++++++++-
16  2 files changed, 234 insertions(+), 4 deletions(-)
17  create mode 100644 Documentation/devicetree/bindings/net/marvell-orion-net.txt
18
19 --- /dev/null
20 +++ b/Documentation/devicetree/bindings/net/marvell-orion-net.txt
21 @@ -0,0 +1,85 @@
22 +Marvell Orion/Discovery ethernet controller
23 +=============================================
24 +
25 +The Marvell Discovery ethernet controller can be found on Marvell Orion SoCs
26 +(Kirkwood, Dove, Orion5x, and Discovery Innovation) and as part of Marvell
27 +Discovery system controller chips (mv64[345]60).
28 +
29 +The Discovery ethernet controller is described with two levels of nodes. The
30 +first level describes the ethernet controller itself and the second level
31 +describes up to 3 ethernet port nodes within that controller. The reason for
32 +the multiple levels is that the port registers are interleaved within a single
33 +set of controller registers. Each port node describes port-specific properties.
34 +
35 +Note: The above separation is only true for Discovery system controllers.
36 +For Orion SoCs we stick to the separation, although there each controller has
37 +only one port associated. Multiple ports are implemented as multiple single-port
38 +controllers. As Kirkwood has some issues with proper initialization after reset,
39 +an extra compatible string is added for it.
40 +
41 +* Ethernet controller node
42 +
43 +Required controller properties:
44 + - #address-cells: shall be 1.
45 + - #size-cells: shall be 0.
46 + - compatible: shall be one of "marvell,orion-eth", "marvell,kirkwood-eth".
47 + - reg: address and length of the controller registers.
48 +
49 +Optional controller properties:
50 + - clocks: phandle reference to the controller clock.
51 + - marvell,tx-checksum-limit: max tx packet size for hardware checksum.
52 +
53 +* Ethernet port node
54 +
55 +Required port properties:
56 + - device_type: shall be "network".
57 + - compatible: shall be one of "marvell,orion-eth-port",
58 +      "marvell,kirkwood-eth-port".
59 + - reg: port number relative to ethernet controller, shall be 0, 1, or 2.
60 + - interrupts: port interrupt.
61 + - local-mac-address: 6 bytes MAC address.
62 +
63 +Optional port properties:
64 + - marvell,tx-queue-size: size of the transmit ring buffer.
65 + - marvell,tx-sram-addr: address of transmit descriptor buffer located in SRAM.
66 + - marvell,tx-sram-size: size of transmit descriptor buffer located in SRAM.
67 + - marvell,rx-queue-size: size of the receive ring buffer.
68 + - marvell,rx-sram-addr: address of receive descriptor buffer located in SRAM.
69 + - marvell,rx-sram-size: size of receive descriptor buffer located in SRAM.
70 +
71 +and
72 +
73 + - phy-handle: phandle reference to ethernet PHY.
74 +
75 +or
76 +
77 + - speed: port speed if no PHY connected.
78 + - duplex: port mode if no PHY connected.
79 +
80 +* Node example:
81 +
82 +mdio-bus {
83 +       ...
84 +       ethphy: ethernet-phy@8 {
85 +               device_type = "ethernet-phy";
86 +               ...
87 +       };
88 +};
89 +
90 +eth: ethernet-controller@72000 {
91 +       compatible = "marvell,orion-eth";
92 +       #address-cells = <1>;
93 +       #size-cells = <0>;
94 +       reg = <0x72000 0x2000>;
95 +       clocks = <&gate_clk 2>;
96 +       marvell,tx-checksum-limit = <1600>;
97 +
98 +       ethernet@0 {
99 +               device_type = "network";
100 +               compatible = "marvell,orion-eth-port";
101 +               reg = <0>;
102 +               interrupts = <29>;
103 +               phy-handle = <&ethphy>;
104 +               local-mac-address = [00 00 00 00 00 00];
105 +       };
106 +};
107 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c
108 +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c
109 @@ -60,6 +60,9 @@
110  #include <linux/types.h>
111  #include <linux/slab.h>
112  #include <linux/clk.h>
113 +#include <linux/of.h>
114 +#include <linux/of_irq.h>
115 +#include <linux/of_net.h>
116  #include <linux/of_mdio.h>
117  
118  static char mv643xx_eth_driver_name[] = "mv643xx_eth";
119 @@ -2452,13 +2455,148 @@ static void infer_hw_params(struct mv643
120         }
121  }
122  
123 +#if defined(CONFIG_OF)
124 +static const struct of_device_id mv643xx_eth_shared_ids[] = {
125 +       { .compatible = "marvell,orion-eth", },
126 +       { .compatible = "marvell,kirkwood-eth", },
127 +       { }
128 +};
129 +MODULE_DEVICE_TABLE(of, mv643xx_eth_shared_ids);
130 +#endif
131 +
132 +#if defined(CONFIG_OF) && !defined(CONFIG_MV64X60)
133 +#define mv643xx_eth_property(_np, _name, _v)                           \
134 +       do {                                                            \
135 +               u32 tmp;                                                \
136 +               if (!of_property_read_u32(_np, "marvell," _name, &tmp)) \
137 +                       _v = tmp;                                       \
138 +       } while (0)
139 +
140 +static struct platform_device *port_platdev[3];
141 +
142 +static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
143 +                                         struct device_node *pnp)
144 +{
145 +       struct platform_device *ppdev;
146 +       struct mv643xx_eth_platform_data ppd;
147 +       struct resource res;
148 +       const char *mac_addr;
149 +       int ret;
150 +
151 +       memset(&ppd, 0, sizeof(ppd));
152 +       ppd.shared = pdev;
153 +
154 +       memset(&res, 0, sizeof(res));
155 +       if (!of_irq_to_resource(pnp, 0, &res)) {
156 +               dev_err(&pdev->dev, "missing interrupt on %s\n", pnp->name);
157 +               return -EINVAL;
158 +       }
159 +
160 +       if (of_property_read_u32(pnp, "reg", &ppd.port_number)) {
161 +               dev_err(&pdev->dev, "missing reg property on %s\n", pnp->name);
162 +               return -EINVAL;
163 +       }
164 +
165 +       if (ppd.port_number >= 3) {
166 +               dev_err(&pdev->dev, "invalid reg property on %s\n", pnp->name);
167 +               return -EINVAL;
168 +       }
169 +
170 +       mac_addr = of_get_mac_address(pnp);
171 +       if (mac_addr)
172 +               memcpy(ppd.mac_addr, mac_addr, 6);
173 +
174 +       mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size);
175 +       mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr);
176 +       mv643xx_eth_property(pnp, "tx-sram-size", ppd.tx_sram_size);
177 +       mv643xx_eth_property(pnp, "rx-queue-size", ppd.rx_queue_size);
178 +       mv643xx_eth_property(pnp, "rx-sram-addr", ppd.rx_sram_addr);
179 +       mv643xx_eth_property(pnp, "rx-sram-size", ppd.rx_sram_size);
180 +
181 +       ppd.phy_node = of_parse_phandle(pnp, "phy-handle", 0);
182 +       if (!ppd.phy_node) {
183 +               ppd.phy_addr = MV643XX_ETH_PHY_NONE;
184 +               of_property_read_u32(pnp, "speed", &ppd.speed);
185 +               of_property_read_u32(pnp, "duplex", &ppd.duplex);
186 +       }
187 +
188 +       ppdev = platform_device_alloc(MV643XX_ETH_NAME, ppd.port_number);
189 +       if (!ppdev)
190 +               return -ENOMEM;
191 +       ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
192 +
193 +       ret = platform_device_add_resources(ppdev, &res, 1);
194 +       if (ret)
195 +               goto port_err;
196 +
197 +       ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd));
198 +       if (ret)
199 +               goto port_err;
200 +
201 +       ret = platform_device_add(ppdev);
202 +       if (ret)
203 +               goto port_err;
204 +
205 +       port_platdev[ppd.port_number] = ppdev;
206 +
207 +       return 0;
208 +
209 +port_err:
210 +       platform_device_put(ppdev);
211 +       return ret;
212 +}
213 +
214 +static int mv643xx_eth_shared_of_probe(struct platform_device *pdev)
215 +{
216 +       struct mv643xx_eth_shared_platform_data *pd;
217 +       struct device_node *pnp, *np = pdev->dev.of_node;
218 +       int ret;
219 +
220 +       /* bail out if not registered from DT */
221 +       if (!np)
222 +               return 0;
223 +
224 +       pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
225 +       if (!pd)
226 +               return -ENOMEM;
227 +       pdev->dev.platform_data = pd;
228 +
229 +       mv643xx_eth_property(np, "tx-checksum-limit", pd->tx_csum_limit);
230 +
231 +       for_each_available_child_of_node(np, pnp) {
232 +               ret = mv643xx_eth_shared_of_add_port(pdev, pnp);
233 +               if (ret)
234 +                       return ret;
235 +       }
236 +       return 0;
237 +}
238 +
239 +static void mv643xx_eth_shared_of_remove(void)
240 +{
241 +       int n;
242 +
243 +       for (n = 0; n < 3; n++) {
244 +               platform_device_del(port_platdev[n]);
245 +               port_platdev[n] = NULL;
246 +       }
247 +}
248 +#else
249 +static int mv643xx_eth_shared_of_probe(struct platform_device *pdev)
250 +{
251 +       return 0
252 +}
253 +
254 +#define mv643xx_eth_shared_of_remove()
255 +#endif
256 +
257  static int mv643xx_eth_shared_probe(struct platform_device *pdev)
258  {
259         static int mv643xx_eth_version_printed;
260 -       struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
261 +       struct mv643xx_eth_shared_platform_data *pd;
262         struct mv643xx_eth_shared_private *msp;
263         const struct mbus_dram_target_info *dram;
264         struct resource *res;
265 +       int ret;
266  
267         if (!mv643xx_eth_version_printed++)
268                 pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n",
269 @@ -2471,6 +2609,7 @@ static int mv643xx_eth_shared_probe(stru
270         msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL);
271         if (msp == NULL)
272                 return -ENOMEM;
273 +       platform_set_drvdata(pdev, msp);
274  
275         msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
276         if (msp->base == NULL)
277 @@ -2487,12 +2626,15 @@ static int mv643xx_eth_shared_probe(stru
278         if (dram)
279                 mv643xx_eth_conf_mbus_windows(msp, dram);
280  
281 +       ret = mv643xx_eth_shared_of_probe(pdev);
282 +       if (ret)
283 +               return ret;
284 +       pd = pdev->dev.platform_data;
285 +
286         msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ?
287                                         pd->tx_csum_limit : 9 * 1024;
288         infer_hw_params(msp);
289  
290 -       platform_set_drvdata(pdev, msp);
291 -
292         return 0;
293  }
294  
295 @@ -2500,9 +2642,9 @@ static int mv643xx_eth_shared_remove(str
296  {
297         struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
298  
299 +       mv643xx_eth_shared_of_remove();
300         if (!IS_ERR(msp->clk))
301                 clk_disable_unprepare(msp->clk);
302 -
303         return 0;
304  }
305  
306 @@ -2512,6 +2654,7 @@ static struct platform_driver mv643xx_et
307         .driver = {
308                 .name   = MV643XX_ETH_SHARED_NAME,
309                 .owner  = THIS_MODULE,
310 +               .of_match_table = of_match_ptr(mv643xx_eth_shared_ids),
311         },
312  };
313  
314 @@ -2720,6 +2863,8 @@ static int mv643xx_eth_probe(struct plat
315         if (!IS_ERR(mp->clk)) {
316                 clk_prepare_enable(mp->clk);
317                 mp->t_clk = clk_get_rate(mp->clk);
318 +       } else if (!IS_ERR(mp->shared->clk)) {
319 +               mp->t_clk = clk_get_rate(mp->shared->clk);
320         }
321  
322         set_params(mp, pd);