net: mv643xx_eth: add DT parsing support
authorSebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Wed, 29 May 2013 09:32:48 +0000 (09:32 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 31 May 2013 00:54:04 +0000 (17:54 -0700)
This adds device tree parsing support for the shared driver of mv643xx_eth.
As the bindings are slightly different from current PPC bindings new binding
documentation is also added. Following PPC-style device setup, the shared
driver now also adds port platform_devices and sets up port platform_data.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/devicetree/bindings/net/marvell-orion-net.txt [new file with mode: 0644]
drivers/net/ethernet/marvell/mv643xx_eth.c

diff --git a/Documentation/devicetree/bindings/net/marvell-orion-net.txt b/Documentation/devicetree/bindings/net/marvell-orion-net.txt
new file mode 100644 (file)
index 0000000..a73b79f
--- /dev/null
@@ -0,0 +1,85 @@
+Marvell Orion/Discovery ethernet controller
+=============================================
+
+The Marvell Discovery ethernet controller can be found on Marvell Orion SoCs
+(Kirkwood, Dove, Orion5x, and Discovery Innovation) and as part of Marvell
+Discovery system controller chips (mv64[345]60).
+
+The Discovery ethernet controller is described with two levels of nodes. The
+first level describes the ethernet controller itself and the second level
+describes up to 3 ethernet port nodes within that controller. The reason for
+the multiple levels is that the port registers are interleaved within a single
+set of controller registers. Each port node describes port-specific properties.
+
+Note: The above separation is only true for Discovery system controllers.
+For Orion SoCs we stick to the separation, although there each controller has
+only one port associated. Multiple ports are implemented as multiple single-port
+controllers. As Kirkwood has some issues with proper initialization after reset,
+an extra compatible string is added for it.
+
+* Ethernet controller node
+
+Required controller properties:
+ - #address-cells: shall be 1.
+ - #size-cells: shall be 0.
+ - compatible: shall be one of "marvell,orion-eth", "marvell,kirkwood-eth".
+ - reg: address and length of the controller registers.
+
+Optional controller properties:
+ - clocks: phandle reference to the controller clock.
+ - marvell,tx-checksum-limit: max tx packet size for hardware checksum.
+
+* Ethernet port node
+
+Required port properties:
+ - device_type: shall be "network".
+ - compatible: shall be one of "marvell,orion-eth-port",
+      "marvell,kirkwood-eth-port".
+ - reg: port number relative to ethernet controller, shall be 0, 1, or 2.
+ - interrupts: port interrupt.
+ - local-mac-address: 6 bytes MAC address.
+
+Optional port properties:
+ - marvell,tx-queue-size: size of the transmit ring buffer.
+ - marvell,tx-sram-addr: address of transmit descriptor buffer located in SRAM.
+ - marvell,tx-sram-size: size of transmit descriptor buffer located in SRAM.
+ - marvell,rx-queue-size: size of the receive ring buffer.
+ - marvell,rx-sram-addr: address of receive descriptor buffer located in SRAM.
+ - marvell,rx-sram-size: size of receive descriptor buffer located in SRAM.
+
+and
+
+ - phy-handle: phandle reference to ethernet PHY.
+
+or
+
+ - speed: port speed if no PHY connected.
+ - duplex: port mode if no PHY connected.
+
+* Node example:
+
+mdio-bus {
+       ...
+       ethphy: ethernet-phy@8 {
+               device_type = "ethernet-phy";
+               ...
+       };
+};
+
+eth: ethernet-controller@72000 {
+       compatible = "marvell,orion-eth";
+       #address-cells = <1>;
+       #size-cells = <0>;
+       reg = <0x72000 0x2000>;
+       clocks = <&gate_clk 2>;
+       marvell,tx-checksum-limit = <1600>;
+
+       ethernet@0 {
+               device_type = "network";
+               compatible = "marvell,orion-eth-port";
+               reg = <0>;
+               interrupts = <29>;
+               phy-handle = <&ethphy>;
+               local-mac-address = [00 00 00 00 00 00];
+       };
+};
index 1b31d752ff90a26a65cac5395ee3ee7ab5c45c6a..23ea7b6e23f1665bda8c726188c2f098d747939a 100644 (file)
@@ -60,6 +60,9 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_net.h>
 #include <linux/of_mdio.h>
 
 static char mv643xx_eth_driver_name[] = "mv643xx_eth";
@@ -2453,13 +2456,148 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp)
        }
 }
 
+#if defined(CONFIG_OF)
+static const struct of_device_id mv643xx_eth_shared_ids[] = {
+       { .compatible = "marvell,orion-eth", },
+       { .compatible = "marvell,kirkwood-eth", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mv643xx_eth_shared_ids);
+#endif
+
+#if defined(CONFIG_OF) && !defined(CONFIG_MV64X60)
+#define mv643xx_eth_property(_np, _name, _v)                           \
+       do {                                                            \
+               u32 tmp;                                                \
+               if (!of_property_read_u32(_np, "marvell," _name, &tmp)) \
+                       _v = tmp;                                       \
+       } while (0)
+
+static struct platform_device *port_platdev[3];
+
+static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev,
+                                         struct device_node *pnp)
+{
+       struct platform_device *ppdev;
+       struct mv643xx_eth_platform_data ppd;
+       struct resource res;
+       const char *mac_addr;
+       int ret;
+
+       memset(&ppd, 0, sizeof(ppd));
+       ppd.shared = pdev;
+
+       memset(&res, 0, sizeof(res));
+       if (!of_irq_to_resource(pnp, 0, &res)) {
+               dev_err(&pdev->dev, "missing interrupt on %s\n", pnp->name);
+               return -EINVAL;
+       }
+
+       if (of_property_read_u32(pnp, "reg", &ppd.port_number)) {
+               dev_err(&pdev->dev, "missing reg property on %s\n", pnp->name);
+               return -EINVAL;
+       }
+
+       if (ppd.port_number >= 3) {
+               dev_err(&pdev->dev, "invalid reg property on %s\n", pnp->name);
+               return -EINVAL;
+       }
+
+       mac_addr = of_get_mac_address(pnp);
+       if (mac_addr)
+               memcpy(ppd.mac_addr, mac_addr, 6);
+
+       mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size);
+       mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr);
+       mv643xx_eth_property(pnp, "tx-sram-size", ppd.tx_sram_size);
+       mv643xx_eth_property(pnp, "rx-queue-size", ppd.rx_queue_size);
+       mv643xx_eth_property(pnp, "rx-sram-addr", ppd.rx_sram_addr);
+       mv643xx_eth_property(pnp, "rx-sram-size", ppd.rx_sram_size);
+
+       ppd.phy_node = of_parse_phandle(pnp, "phy-handle", 0);
+       if (!ppd.phy_node) {
+               ppd.phy_addr = MV643XX_ETH_PHY_NONE;
+               of_property_read_u32(pnp, "speed", &ppd.speed);
+               of_property_read_u32(pnp, "duplex", &ppd.duplex);
+       }
+
+       ppdev = platform_device_alloc(MV643XX_ETH_NAME, ppd.port_number);
+       if (!ppdev)
+               return -ENOMEM;
+       ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+       ret = platform_device_add_resources(ppdev, &res, 1);
+       if (ret)
+               goto port_err;
+
+       ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd));
+       if (ret)
+               goto port_err;
+
+       ret = platform_device_add(ppdev);
+       if (ret)
+               goto port_err;
+
+       port_platdev[ppd.port_number] = ppdev;
+
+       return 0;
+
+port_err:
+       platform_device_put(ppdev);
+       return ret;
+}
+
+static int mv643xx_eth_shared_of_probe(struct platform_device *pdev)
+{
+       struct mv643xx_eth_shared_platform_data *pd;
+       struct device_node *pnp, *np = pdev->dev.of_node;
+       int ret;
+
+       /* bail out if not registered from DT */
+       if (!np)
+               return 0;
+
+       pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
+       if (!pd)
+               return -ENOMEM;
+       pdev->dev.platform_data = pd;
+
+       mv643xx_eth_property(np, "tx-checksum-limit", pd->tx_csum_limit);
+
+       for_each_available_child_of_node(np, pnp) {
+               ret = mv643xx_eth_shared_of_add_port(pdev, pnp);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static void mv643xx_eth_shared_of_remove(void)
+{
+       int n;
+
+       for (n = 0; n < 3; n++) {
+               platform_device_del(port_platdev[n]);
+               port_platdev[n] = NULL;
+       }
+}
+#else
+static int mv643xx_eth_shared_of_probe(struct platform_device *pdev)
+{
+       return 0
+}
+
+#define mv643xx_eth_shared_of_remove()
+#endif
+
 static int mv643xx_eth_shared_probe(struct platform_device *pdev)
 {
        static int mv643xx_eth_version_printed;
-       struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
+       struct mv643xx_eth_shared_platform_data *pd;
        struct mv643xx_eth_shared_private *msp;
        const struct mbus_dram_target_info *dram;
        struct resource *res;
+       int ret;
 
        if (!mv643xx_eth_version_printed++)
                pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n",
@@ -2472,6 +2610,7 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
        msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL);
        if (msp == NULL)
                return -ENOMEM;
+       platform_set_drvdata(pdev, msp);
 
        msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
        if (msp->base == NULL)
@@ -2488,12 +2627,15 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
        if (dram)
                mv643xx_eth_conf_mbus_windows(msp, dram);
 
+       ret = mv643xx_eth_shared_of_probe(pdev);
+       if (ret)
+               return ret;
+       pd = pdev->dev.platform_data;
+
        msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ?
                                        pd->tx_csum_limit : 9 * 1024;
        infer_hw_params(msp);
 
-       platform_set_drvdata(pdev, msp);
-
        return 0;
 }
 
@@ -2501,9 +2643,9 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev)
 {
        struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
 
+       mv643xx_eth_shared_of_remove();
        if (!IS_ERR(msp->clk))
                clk_disable_unprepare(msp->clk);
-
        return 0;
 }
 
@@ -2513,6 +2655,7 @@ static struct platform_driver mv643xx_eth_shared_driver = {
        .driver = {
                .name   = MV643XX_ETH_SHARED_NAME,
                .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(mv643xx_eth_shared_ids),
        },
 };
 
@@ -2721,6 +2864,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
        if (!IS_ERR(mp->clk)) {
                clk_prepare_enable(mp->clk);
                mp->t_clk = clk_get_rate(mp->clk);
+       } else if (!IS_ERR(mp->shared->clk)) {
+               mp->t_clk = clk_get_rate(mp->shared->clk);
        }
 
        set_params(mp, pd);