[PATCH v7] ethernet: arc: Add support for Rockchip SoC layer device tree bindings

PERIER Romain romain.perier at gmail.com
Tue Sep 9 03:16:58 PDT 2014


changes since v5 and v6 (v6 was wrong sorry):
- Warnings in kconfig are fixed by "net: phy: mdio-sun4i: don't select
REGULATOR" (commit id 97a13e5289baa96eaddd06e61d277457d837af3a) in
net-next
- in emac_rockchip_remove: I moved arc_emac_remove before
clk_disable_unprepare. It does not make sense to free resources before
disconnect from phy and calling the core driver.

2014-09-09 12:15 GMT+02:00 Romain Perier <romain.perier at gmail.com>:
> This patch defines a platform glue layer for Rockchip SoCs which
> support arc-emac driver. It ensures that regulator for the rmii is on
> before trying to connect to the ethernet controller. It applies right
> speed and mode changes to the grf when ethernet settings change.
>
> Signed-off-by: Romain Perier <romain.perier at gmail.com>
> ---
>  drivers/net/ethernet/arc/Kconfig         |  10 ++
>  drivers/net/ethernet/arc/Makefile        |   1 +
>  drivers/net/ethernet/arc/emac.h          |   4 +-
>  drivers/net/ethernet/arc/emac_main.c     |   2 +
>  drivers/net/ethernet/arc/emac_rockchip.c | 229 +++++++++++++++++++++++++++++++
>  5 files changed, 245 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/net/ethernet/arc/emac_rockchip.c
>
> diff --git a/drivers/net/ethernet/arc/Kconfig b/drivers/net/ethernet/arc/Kconfig
> index 89e04fd..8e262e2 100644
> --- a/drivers/net/ethernet/arc/Kconfig
> +++ b/drivers/net/ethernet/arc/Kconfig
> @@ -32,4 +32,14 @@ config ARC_EMAC
>           non-standard on-chip ethernet device ARC EMAC 10/100 is used.
>           Say Y here if you have such a board.  If unsure, say N.
>
> +config EMAC_ROCKCHIP
> +       tristate "Rockchip EMAC support"
> +       select ARC_EMAC_CORE
> +       depends on OF_IRQ && OF_NET && REGULATOR
> +       ---help---
> +         Support for Rockchip RK3066/RK3188 EMAC ethernet controllers.
> +         This selects Rockchip SoC glue layer support for the
> +         emac device driver. This driver is used for RK3066/RK3188
> +         EMAC ethernet controller.
> +
>  endif # NET_VENDOR_ARC
> diff --git a/drivers/net/ethernet/arc/Makefile b/drivers/net/ethernet/arc/Makefile
> index 241bb80..79108af 100644
> --- a/drivers/net/ethernet/arc/Makefile
> +++ b/drivers/net/ethernet/arc/Makefile
> @@ -5,3 +5,4 @@
>  arc_emac-objs := emac_main.o emac_mdio.o
>  obj-$(CONFIG_ARC_EMAC_CORE) += arc_emac.o
>  obj-$(CONFIG_ARC_EMAC) += emac_arc.o
> +obj-$(CONFIG_EMAC_ROCKCHIP) += emac_rockchip.o
> diff --git a/drivers/net/ethernet/arc/emac.h b/drivers/net/ethernet/arc/emac.h
> index eb2ba67..dae1ac3 100644
> --- a/drivers/net/ethernet/arc/emac.h
> +++ b/drivers/net/ethernet/arc/emac.h
> @@ -123,9 +123,11 @@ struct buffer_state {
>   * @speed:     PHY's last set speed.
>   */
>  struct arc_emac_priv {
> -       /* Devices */
>         const char *drv_name;
>         const char *drv_version;
> +       void (*set_mac_speed)(void *priv, unsigned int speed);
> +
> +       /* Devices */
>         struct device *dev;
>         struct phy_device *phy_dev;
>         struct mii_bus *bus;
> diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c
> index b35c69e..a08f343 100644
> --- a/drivers/net/ethernet/arc/emac_main.c
> +++ b/drivers/net/ethernet/arc/emac_main.c
> @@ -48,6 +48,8 @@ static void arc_emac_adjust_link(struct net_device *ndev)
>         if (priv->speed != phy_dev->speed) {
>                 priv->speed = phy_dev->speed;
>                 state_changed = 1;
> +               if (priv->set_mac_speed)
> +                       priv->set_mac_speed(priv, priv->speed);
>         }
>
>         if (priv->duplex != phy_dev->duplex) {
> diff --git a/drivers/net/ethernet/arc/emac_rockchip.c b/drivers/net/ethernet/arc/emac_rockchip.c
> new file mode 100644
> index 0000000..c31c740
> --- /dev/null
> +++ b/drivers/net/ethernet/arc/emac_rockchip.c
> @@ -0,0 +1,229 @@
> +/**
> + * emac-rockchip.c - Rockchip EMAC specific glue layer
> + *
> + * Copyright (C) 2014 Romain Perier <romain.perier at gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/etherdevice.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of_net.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +
> +#include "emac.h"
> +
> +#define DRV_NAME        "rockchip_emac"
> +#define DRV_VERSION     "1.0"
> +
> +#define GRF_MODE_MII           (1UL << 0)
> +#define GRF_MODE_RMII          (0UL << 0)
> +#define GRF_SPEED_10M          (0UL << 1)
> +#define GRF_SPEED_100M         (1UL << 1)
> +#define GRF_SPEED_ENABLE_BIT   (1UL << 17)
> +#define GRF_MODE_ENABLE_BIT    (1UL << 16)
> +
> +struct emac_rockchip_soc_data {
> +       int grf_offset;
> +};
> +
> +struct rockchip_priv_data {
> +       struct arc_emac_priv emac;
> +       struct regmap *grf;
> +       const struct emac_rockchip_soc_data *soc_data;
> +       struct regulator *regulator;
> +       struct clk *refclk;
> +};
> +
> +static void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
> +{
> +       struct rockchip_priv_data *emac = priv;
> +       u32 data;
> +       int err = 0;
> +
> +       /* write-enable bits */
> +       data = GRF_SPEED_ENABLE_BIT;
> +
> +       switch(speed) {
> +       case 10:
> +               data |= GRF_SPEED_10M;
> +               break;
> +       case 100:
> +               data |= GRF_SPEED_100M;
> +               break;
> +       default:
> +               pr_err("speed %u not supported\n", speed);
> +               return;
> +       }
> +
> +       err = regmap_write(emac->grf, emac->soc_data->grf_offset, data);
> +       if (err)
> +               pr_err("unable to apply speed %u to grf (%d)\n", speed, err);
> +}
> +
> +static const struct emac_rockchip_soc_data emac_rockchip_dt_data[] = {
> +       { .grf_offset = 0x154 }, /* rk3066 */
> +       { .grf_offset = 0x0a4 }, /* rk3188 */
> +};
> +
> +static const struct of_device_id emac_rockchip_dt_ids[] = {
> +       { .compatible = "rockchip,rk3066-emac", .data = &emac_rockchip_dt_data[0] },
> +       { .compatible = "rockchip,rk3188-emac", .data = &emac_rockchip_dt_data[1] },
> +       { /* Sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, emac_rockchip_dt_ids);
> +
> +static int emac_rockchip_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct net_device *ndev;
> +       struct rockchip_priv_data *priv;
> +       const struct of_device_id *match;
> +       u32 data;
> +       int err, interface;
> +
> +       if (!pdev->dev.of_node)
> +               return -ENODEV;
> +
> +       ndev = alloc_etherdev(sizeof(struct rockchip_priv_data));
> +       if (!ndev)
> +               return -ENOMEM;
> +       platform_set_drvdata(pdev, ndev);
> +       SET_NETDEV_DEV(ndev, dev);
> +
> +       priv = netdev_priv(ndev);
> +       priv->emac.drv_name = DRV_NAME;
> +       priv->emac.drv_version = DRV_VERSION;
> +       priv->emac.set_mac_speed = emac_rockchip_set_mac_speed;
> +
> +       interface = of_get_phy_mode(dev->of_node);
> +
> +       /* RK3066 and RK3188 SoCs only support RMII */
> +       if (interface != PHY_INTERFACE_MODE_RMII) {
> +               dev_err(dev, "unsupported phy interface mode %d\n", interface);
> +               err = -ENOTSUPP;
> +               goto out_netdev;
> +       }
> +
> +       priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
> +       if (IS_ERR(priv->grf)) {
> +               dev_err(dev, "failed to retrieve global register file (%ld)\n", PTR_ERR(priv->grf));
> +               err = PTR_ERR(priv->grf);
> +               goto out_netdev;
> +       }
> +
> +       match = of_match_node(emac_rockchip_dt_ids, dev->of_node);
> +       priv->soc_data = match->data;
> +
> +       priv->emac.clk = devm_clk_get(dev, "hclk");
> +       if (IS_ERR(priv->emac.clk)) {
> +               dev_err(dev, "failed to retrieve host clock (%ld)\n", PTR_ERR(priv->emac.clk));
> +               err = PTR_ERR(priv->emac.clk);
> +               goto out_netdev;
> +       }
> +
> +       priv->refclk = devm_clk_get(dev, "macref");
> +       if (IS_ERR(priv->refclk)) {
> +               dev_err(dev, "failed to retrieve reference clock (%ld)\n", PTR_ERR(priv->refclk));
> +               err = PTR_ERR(priv->refclk);
> +               goto out_netdev;
> +       }
> +
> +       err = clk_prepare_enable(priv->refclk);
> +       if (err) {
> +               dev_err(dev, "failed to enable reference clock (%d)\n", err);
> +               goto out_netdev;
> +       }
> +
> +       /* Optional regulator for PHY */
> +       priv->regulator = devm_regulator_get_optional(dev, "phy");
> +       if (IS_ERR(priv->regulator)) {
> +               if (PTR_ERR(priv->regulator) == -EPROBE_DEFER)
> +                       return -EPROBE_DEFER;
> +               dev_err(dev, "no regulator found\n");
> +               priv->regulator = NULL;
> +       }
> +
> +       if (priv->regulator) {
> +               err = regulator_enable(priv->regulator);
> +               if (err) {
> +                       dev_err(dev, "failed to enable phy-supply (%d)\n", err);
> +                       goto out_clk_disable;
> +               }
> +       }
> +
> +       err = arc_emac_probe(ndev, interface);
> +       if (err)
> +               goto out_regulator_disable;
> +
> +       /* write-enable bits */
> +       data = GRF_MODE_ENABLE_BIT | GRF_SPEED_ENABLE_BIT;
> +
> +       data |= GRF_SPEED_100M;
> +       data |= GRF_MODE_RMII;
> +
> +       err = regmap_write(priv->grf, priv->soc_data->grf_offset, data);
> +       if (err) {
> +               dev_err(dev, "unable to apply initial settings to grf (%d)\n", err);
> +               goto out_regulator_disable;
> +       }
> +
> +       /* RMII interface needs always a rate of 50MHz */
> +       err = clk_set_rate(priv->refclk, 50000000);
> +       if (err)
> +               dev_err(dev, "failed to change reference clock rate (%d)\n", err);
> +       return 0;
> +
> +out_regulator_disable:
> +       if (priv->regulator)
> +               regulator_disable(priv->regulator);
> +out_clk_disable:
> +       clk_disable_unprepare(priv->refclk);
> +out_netdev:
> +       free_netdev(ndev);
> +       return err;
> +}
> +
> +static int emac_rockchip_remove(struct platform_device *pdev)
> +{
> +       struct net_device *ndev = platform_get_drvdata(pdev);
> +       struct rockchip_priv_data *priv = netdev_priv(ndev);
> +       int err;
> +
> +       err = arc_emac_remove(ndev);
> +
> +       clk_disable_unprepare(priv->refclk);
> +
> +       if (priv->regulator)
> +               regulator_disable(priv->regulator);
> +
> +       free_netdev(ndev);
> +       return err;
> +}
> +
> +static struct platform_driver emac_rockchip_driver = {
> +       .probe = emac_rockchip_probe,
> +       .remove = emac_rockchip_remove,
> +       .driver = {
> +               .name = DRV_NAME,
> +               .of_match_table  = emac_rockchip_dt_ids,
> +       },
> +};
> +
> +module_platform_driver(emac_rockchip_driver);
> +
> +MODULE_AUTHOR("Romain Perier <romain.perier at gmail.com>");
> +MODULE_DESCRIPTION("Rockchip EMAC platform driver");
> +MODULE_LICENSE("GPL");
> --
> 1.9.1
>



More information about the linux-arm-kernel mailing list