net: mv643xx_eth: add DT parsing support

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>
This commit is contained in:
Sebastian Hesselbarth 2013-05-29 09:32:48 +00:00 committed by David S. Miller
parent cb85215ffc
commit 76723bca28
2 changed files with 234 additions and 4 deletions

View File

@ -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];
};
};

View 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);