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:
parent
cb85215ffc
commit
76723bca28
85
Documentation/devicetree/bindings/net/marvell-orion-net.txt
Normal file
85
Documentation/devicetree/bindings/net/marvell-orion-net.txt
Normal 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 = <ðphy>;
|
||||||
|
local-mac-address = [00 00 00 00 00 00];
|
||||||
|
};
|
||||||
|
};
|
@ -60,6 +60,9 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
#include <linux/of_net.h>
|
||||||
#include <linux/of_mdio.h>
|
#include <linux/of_mdio.h>
|
||||||
|
|
||||||
static char mv643xx_eth_driver_name[] = "mv643xx_eth";
|
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_shared_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
static int mv643xx_eth_version_printed;
|
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;
|
struct mv643xx_eth_shared_private *msp;
|
||||||
const struct mbus_dram_target_info *dram;
|
const struct mbus_dram_target_info *dram;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!mv643xx_eth_version_printed++)
|
if (!mv643xx_eth_version_printed++)
|
||||||
pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n",
|
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);
|
msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL);
|
||||||
if (msp == NULL)
|
if (msp == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
platform_set_drvdata(pdev, msp);
|
||||||
|
|
||||||
msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||||
if (msp->base == NULL)
|
if (msp->base == NULL)
|
||||||
@ -2488,12 +2627,15 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
|
|||||||
if (dram)
|
if (dram)
|
||||||
mv643xx_eth_conf_mbus_windows(msp, 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) ?
|
msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ?
|
||||||
pd->tx_csum_limit : 9 * 1024;
|
pd->tx_csum_limit : 9 * 1024;
|
||||||
infer_hw_params(msp);
|
infer_hw_params(msp);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, msp);
|
|
||||||
|
|
||||||
return 0;
|
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);
|
struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
mv643xx_eth_shared_of_remove();
|
||||||
if (!IS_ERR(msp->clk))
|
if (!IS_ERR(msp->clk))
|
||||||
clk_disable_unprepare(msp->clk);
|
clk_disable_unprepare(msp->clk);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2513,6 +2655,7 @@ static struct platform_driver mv643xx_eth_shared_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = MV643XX_ETH_SHARED_NAME,
|
.name = MV643XX_ETH_SHARED_NAME,
|
||||||
.owner = THIS_MODULE,
|
.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)) {
|
if (!IS_ERR(mp->clk)) {
|
||||||
clk_prepare_enable(mp->clk);
|
clk_prepare_enable(mp->clk);
|
||||||
mp->t_clk = clk_get_rate(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);
|
set_params(mp, pd);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user