diff options
-rw-r--r-- | Documentation/devicetree/bindings/net/marvell-orion-net.txt | 85 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mv643xx_eth.c | 153 |
2 files changed, 234 insertions, 4 deletions
diff --git a/Documentation/devicetree/bindings/net/marvell-orion-net.txt b/Documentation/devicetree/bindings/net/marvell-orion-net.txt new file mode 100644 index 000000000000..a73b79f227e1 --- /dev/null +++ b/Documentation/devicetree/bindings/net/marvell-orion-net.txt | |||
@@ -0,0 +1,85 @@ | |||
1 | Marvell Orion/Discovery ethernet controller | ||
2 | ============================================= | ||
3 | |||
4 | The Marvell Discovery ethernet controller can be found on Marvell Orion SoCs | ||
5 | (Kirkwood, Dove, Orion5x, and Discovery Innovation) and as part of Marvell | ||
6 | Discovery system controller chips (mv64[345]60). | ||
7 | |||
8 | The Discovery ethernet controller is described with two levels of nodes. The | ||
9 | first level describes the ethernet controller itself and the second level | ||
10 | describes up to 3 ethernet port nodes within that controller. The reason for | ||
11 | the multiple levels is that the port registers are interleaved within a single | ||
12 | set of controller registers. Each port node describes port-specific properties. | ||
13 | |||
14 | Note: The above separation is only true for Discovery system controllers. | ||
15 | For Orion SoCs we stick to the separation, although there each controller has | ||
16 | only one port associated. Multiple ports are implemented as multiple single-port | ||
17 | controllers. As Kirkwood has some issues with proper initialization after reset, | ||
18 | an extra compatible string is added for it. | ||
19 | |||
20 | * Ethernet controller node | ||
21 | |||
22 | Required controller properties: | ||
23 | - #address-cells: shall be 1. | ||
24 | - #size-cells: shall be 0. | ||
25 | - compatible: shall be one of "marvell,orion-eth", "marvell,kirkwood-eth". | ||
26 | - reg: address and length of the controller registers. | ||
27 | |||
28 | Optional controller properties: | ||
29 | - clocks: phandle reference to the controller clock. | ||
30 | - marvell,tx-checksum-limit: max tx packet size for hardware checksum. | ||
31 | |||
32 | * Ethernet port node | ||
33 | |||
34 | Required port properties: | ||
35 | - device_type: shall be "network". | ||
36 | - compatible: shall be one of "marvell,orion-eth-port", | ||
37 | "marvell,kirkwood-eth-port". | ||
38 | - reg: port number relative to ethernet controller, shall be 0, 1, or 2. | ||
39 | - interrupts: port interrupt. | ||
40 | - local-mac-address: 6 bytes MAC address. | ||
41 | |||
42 | Optional port properties: | ||
43 | - marvell,tx-queue-size: size of the transmit ring buffer. | ||
44 | - marvell,tx-sram-addr: address of transmit descriptor buffer located in SRAM. | ||
45 | - marvell,tx-sram-size: size of transmit descriptor buffer located in SRAM. | ||
46 | - marvell,rx-queue-size: size of the receive ring buffer. | ||
47 | - marvell,rx-sram-addr: address of receive descriptor buffer located in SRAM. | ||
48 | - marvell,rx-sram-size: size of receive descriptor buffer located in SRAM. | ||
49 | |||
50 | and | ||
51 | |||
52 | - phy-handle: phandle reference to ethernet PHY. | ||
53 | |||
54 | or | ||
55 | |||
56 | - speed: port speed if no PHY connected. | ||
57 | - duplex: port mode if no PHY connected. | ||
58 | |||
59 | * Node example: | ||
60 | |||
61 | mdio-bus { | ||
62 | ... | ||
63 | ethphy: ethernet-phy@8 { | ||
64 | device_type = "ethernet-phy"; | ||
65 | ... | ||
66 | }; | ||
67 | }; | ||
68 | |||
69 | eth: ethernet-controller@72000 { | ||
70 | compatible = "marvell,orion-eth"; | ||
71 | #address-cells = <1>; | ||
72 | #size-cells = <0>; | ||
73 | reg = <0x72000 0x2000>; | ||
74 | clocks = <&gate_clk 2>; | ||
75 | marvell,tx-checksum-limit = <1600>; | ||
76 | |||
77 | ethernet@0 { | ||
78 | device_type = "network"; | ||
79 | compatible = "marvell,orion-eth-port"; | ||
80 | reg = <0>; | ||
81 | interrupts = <29>; | ||
82 | phy-handle = <ðphy>; | ||
83 | local-mac-address = [00 00 00 00 00 00]; | ||
84 | }; | ||
85 | }; | ||
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 1b31d752ff90..23ea7b6e23f1 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c | |||
@@ -60,6 +60,9 @@ | |||
60 | #include <linux/types.h> | 60 | #include <linux/types.h> |
61 | #include <linux/slab.h> | 61 | #include <linux/slab.h> |
62 | #include <linux/clk.h> | 62 | #include <linux/clk.h> |
63 | #include <linux/of.h> | ||
64 | #include <linux/of_irq.h> | ||
65 | #include <linux/of_net.h> | ||
63 | #include <linux/of_mdio.h> | 66 | #include <linux/of_mdio.h> |
64 | 67 | ||
65 | static char mv643xx_eth_driver_name[] = "mv643xx_eth"; | 68 | static char mv643xx_eth_driver_name[] = "mv643xx_eth"; |
@@ -2453,13 +2456,148 @@ static void infer_hw_params(struct mv643xx_eth_shared_private *msp) | |||
2453 | } | 2456 | } |
2454 | } | 2457 | } |
2455 | 2458 | ||
2459 | #if defined(CONFIG_OF) | ||
2460 | static const struct of_device_id mv643xx_eth_shared_ids[] = { | ||
2461 | { .compatible = "marvell,orion-eth", }, | ||
2462 | { .compatible = "marvell,kirkwood-eth", }, | ||
2463 | { } | ||
2464 | }; | ||
2465 | MODULE_DEVICE_TABLE(of, mv643xx_eth_shared_ids); | ||
2466 | #endif | ||
2467 | |||
2468 | #if defined(CONFIG_OF) && !defined(CONFIG_MV64X60) | ||
2469 | #define mv643xx_eth_property(_np, _name, _v) \ | ||
2470 | do { \ | ||
2471 | u32 tmp; \ | ||
2472 | if (!of_property_read_u32(_np, "marvell," _name, &tmp)) \ | ||
2473 | _v = tmp; \ | ||
2474 | } while (0) | ||
2475 | |||
2476 | static struct platform_device *port_platdev[3]; | ||
2477 | |||
2478 | static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, | ||
2479 | struct device_node *pnp) | ||
2480 | { | ||
2481 | struct platform_device *ppdev; | ||
2482 | struct mv643xx_eth_platform_data ppd; | ||
2483 | struct resource res; | ||
2484 | const char *mac_addr; | ||
2485 | int ret; | ||
2486 | |||
2487 | memset(&ppd, 0, sizeof(ppd)); | ||
2488 | ppd.shared = pdev; | ||
2489 | |||
2490 | memset(&res, 0, sizeof(res)); | ||
2491 | if (!of_irq_to_resource(pnp, 0, &res)) { | ||
2492 | dev_err(&pdev->dev, "missing interrupt on %s\n", pnp->name); | ||
2493 | return -EINVAL; | ||
2494 | } | ||
2495 | |||
2496 | if (of_property_read_u32(pnp, "reg", &ppd.port_number)) { | ||
2497 | dev_err(&pdev->dev, "missing reg property on %s\n", pnp->name); | ||
2498 | return -EINVAL; | ||
2499 | } | ||
2500 | |||
2501 | if (ppd.port_number >= 3) { | ||
2502 | dev_err(&pdev->dev, "invalid reg property on %s\n", pnp->name); | ||
2503 | return -EINVAL; | ||
2504 | } | ||
2505 | |||
2506 | mac_addr = of_get_mac_address(pnp); | ||
2507 | if (mac_addr) | ||
2508 | memcpy(ppd.mac_addr, mac_addr, 6); | ||
2509 | |||
2510 | mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size); | ||
2511 | mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr); | ||
2512 | mv643xx_eth_property(pnp, "tx-sram-size", ppd.tx_sram_size); | ||
2513 | mv643xx_eth_property(pnp, "rx-queue-size", ppd.rx_queue_size); | ||
2514 | mv643xx_eth_property(pnp, "rx-sram-addr", ppd.rx_sram_addr); | ||
2515 | mv643xx_eth_property(pnp, "rx-sram-size", ppd.rx_sram_size); | ||
2516 | |||
2517 | ppd.phy_node = of_parse_phandle(pnp, "phy-handle", 0); | ||
2518 | if (!ppd.phy_node) { | ||
2519 | ppd.phy_addr = MV643XX_ETH_PHY_NONE; | ||
2520 | of_property_read_u32(pnp, "speed", &ppd.speed); | ||
2521 | of_property_read_u32(pnp, "duplex", &ppd.duplex); | ||
2522 | } | ||
2523 | |||
2524 | ppdev = platform_device_alloc(MV643XX_ETH_NAME, ppd.port_number); | ||
2525 | if (!ppdev) | ||
2526 | return -ENOMEM; | ||
2527 | ppdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); | ||
2528 | |||
2529 | ret = platform_device_add_resources(ppdev, &res, 1); | ||
2530 | if (ret) | ||
2531 | goto port_err; | ||
2532 | |||
2533 | ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd)); | ||
2534 | if (ret) | ||
2535 | goto port_err; | ||
2536 | |||
2537 | ret = platform_device_add(ppdev); | ||
2538 | if (ret) | ||
2539 | goto port_err; | ||
2540 | |||
2541 | port_platdev[ppd.port_number] = ppdev; | ||
2542 | |||
2543 | return 0; | ||
2544 | |||
2545 | port_err: | ||
2546 | platform_device_put(ppdev); | ||
2547 | return ret; | ||
2548 | } | ||
2549 | |||
2550 | static int mv643xx_eth_shared_of_probe(struct platform_device *pdev) | ||
2551 | { | ||
2552 | struct mv643xx_eth_shared_platform_data *pd; | ||
2553 | struct device_node *pnp, *np = pdev->dev.of_node; | ||
2554 | int ret; | ||
2555 | |||
2556 | /* bail out if not registered from DT */ | ||
2557 | if (!np) | ||
2558 | return 0; | ||
2559 | |||
2560 | pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL); | ||
2561 | if (!pd) | ||
2562 | return -ENOMEM; | ||
2563 | pdev->dev.platform_data = pd; | ||
2564 | |||
2565 | mv643xx_eth_property(np, "tx-checksum-limit", pd->tx_csum_limit); | ||
2566 | |||
2567 | for_each_available_child_of_node(np, pnp) { | ||
2568 | ret = mv643xx_eth_shared_of_add_port(pdev, pnp); | ||
2569 | if (ret) | ||
2570 | return ret; | ||
2571 | } | ||
2572 | return 0; | ||
2573 | } | ||
2574 | |||
2575 | static void mv643xx_eth_shared_of_remove(void) | ||
2576 | { | ||
2577 | int n; | ||
2578 | |||
2579 | for (n = 0; n < 3; n++) { | ||
2580 | platform_device_del(port_platdev[n]); | ||
2581 | port_platdev[n] = NULL; | ||
2582 | } | ||
2583 | } | ||
2584 | #else | ||
2585 | static int mv643xx_eth_shared_of_probe(struct platform_device *pdev) | ||
2586 | { | ||
2587 | return 0 | ||
2588 | } | ||
2589 | |||
2590 | #define mv643xx_eth_shared_of_remove() | ||
2591 | #endif | ||
2592 | |||
2456 | static int mv643xx_eth_shared_probe(struct platform_device *pdev) | 2593 | static int mv643xx_eth_shared_probe(struct platform_device *pdev) |
2457 | { | 2594 | { |
2458 | static int mv643xx_eth_version_printed; | 2595 | static int mv643xx_eth_version_printed; |
2459 | struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data; | 2596 | struct mv643xx_eth_shared_platform_data *pd; |
2460 | struct mv643xx_eth_shared_private *msp; | 2597 | struct mv643xx_eth_shared_private *msp; |
2461 | const struct mbus_dram_target_info *dram; | 2598 | const struct mbus_dram_target_info *dram; |
2462 | struct resource *res; | 2599 | struct resource *res; |
2600 | int ret; | ||
2463 | 2601 | ||
2464 | if (!mv643xx_eth_version_printed++) | 2602 | if (!mv643xx_eth_version_printed++) |
2465 | pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n", | 2603 | 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) | |||
2472 | msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL); | 2610 | msp = devm_kzalloc(&pdev->dev, sizeof(*msp), GFP_KERNEL); |
2473 | if (msp == NULL) | 2611 | if (msp == NULL) |
2474 | return -ENOMEM; | 2612 | return -ENOMEM; |
2613 | platform_set_drvdata(pdev, msp); | ||
2475 | 2614 | ||
2476 | msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); | 2615 | msp->base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); |
2477 | if (msp->base == NULL) | 2616 | if (msp->base == NULL) |
@@ -2488,12 +2627,15 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) | |||
2488 | if (dram) | 2627 | if (dram) |
2489 | mv643xx_eth_conf_mbus_windows(msp, dram); | 2628 | mv643xx_eth_conf_mbus_windows(msp, dram); |
2490 | 2629 | ||
2630 | ret = mv643xx_eth_shared_of_probe(pdev); | ||
2631 | if (ret) | ||
2632 | return ret; | ||
2633 | pd = pdev->dev.platform_data; | ||
2634 | |||
2491 | msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ? | 2635 | msp->tx_csum_limit = (pd != NULL && pd->tx_csum_limit) ? |
2492 | pd->tx_csum_limit : 9 * 1024; | 2636 | pd->tx_csum_limit : 9 * 1024; |
2493 | infer_hw_params(msp); | 2637 | infer_hw_params(msp); |
2494 | 2638 | ||
2495 | platform_set_drvdata(pdev, msp); | ||
2496 | |||
2497 | return 0; | 2639 | return 0; |
2498 | } | 2640 | } |
2499 | 2641 | ||
@@ -2501,9 +2643,9 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) | |||
2501 | { | 2643 | { |
2502 | struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); | 2644 | struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev); |
2503 | 2645 | ||
2646 | mv643xx_eth_shared_of_remove(); | ||
2504 | if (!IS_ERR(msp->clk)) | 2647 | if (!IS_ERR(msp->clk)) |
2505 | clk_disable_unprepare(msp->clk); | 2648 | clk_disable_unprepare(msp->clk); |
2506 | |||
2507 | return 0; | 2649 | return 0; |
2508 | } | 2650 | } |
2509 | 2651 | ||
@@ -2513,6 +2655,7 @@ static struct platform_driver mv643xx_eth_shared_driver = { | |||
2513 | .driver = { | 2655 | .driver = { |
2514 | .name = MV643XX_ETH_SHARED_NAME, | 2656 | .name = MV643XX_ETH_SHARED_NAME, |
2515 | .owner = THIS_MODULE, | 2657 | .owner = THIS_MODULE, |
2658 | .of_match_table = of_match_ptr(mv643xx_eth_shared_ids), | ||
2516 | }, | 2659 | }, |
2517 | }; | 2660 | }; |
2518 | 2661 | ||
@@ -2721,6 +2864,8 @@ static int mv643xx_eth_probe(struct platform_device *pdev) | |||
2721 | if (!IS_ERR(mp->clk)) { | 2864 | if (!IS_ERR(mp->clk)) { |
2722 | clk_prepare_enable(mp->clk); | 2865 | clk_prepare_enable(mp->clk); |
2723 | mp->t_clk = clk_get_rate(mp->clk); | 2866 | mp->t_clk = clk_get_rate(mp->clk); |
2867 | } else if (!IS_ERR(mp->shared->clk)) { | ||
2868 | mp->t_clk = clk_get_rate(mp->shared->clk); | ||
2724 | } | 2869 | } |
2725 | 2870 | ||
2726 | set_params(mp, pd); | 2871 | set_params(mp, pd); |