18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/**
38c2ecf20Sopenharmony_ci * emac-rockchip.c - Rockchip EMAC specific glue layer
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Romain Perier <romain.perier@gmail.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
98c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/of_net.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/regmap.h>
148c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "emac.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define DRV_NAME        "rockchip_emac"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct emac_rockchip_soc_data {
218c2ecf20Sopenharmony_ci	unsigned int grf_offset;
228c2ecf20Sopenharmony_ci	unsigned int grf_mode_offset;
238c2ecf20Sopenharmony_ci	unsigned int grf_speed_offset;
248c2ecf20Sopenharmony_ci	bool need_div_macclk;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct rockchip_priv_data {
288c2ecf20Sopenharmony_ci	struct arc_emac_priv emac;
298c2ecf20Sopenharmony_ci	struct regmap *grf;
308c2ecf20Sopenharmony_ci	const struct emac_rockchip_soc_data *soc_data;
318c2ecf20Sopenharmony_ci	struct regulator *regulator;
328c2ecf20Sopenharmony_ci	struct clk *refclk;
338c2ecf20Sopenharmony_ci	struct clk *macclk;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic void emac_rockchip_set_mac_speed(void *priv, unsigned int speed)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct rockchip_priv_data *emac = priv;
398c2ecf20Sopenharmony_ci	u32 speed_offset = emac->soc_data->grf_speed_offset;
408c2ecf20Sopenharmony_ci	u32 data;
418c2ecf20Sopenharmony_ci	int err = 0;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	switch (speed) {
448c2ecf20Sopenharmony_ci	case 10:
458c2ecf20Sopenharmony_ci		data = (1 << (speed_offset + 16)) | (0 << speed_offset);
468c2ecf20Sopenharmony_ci		break;
478c2ecf20Sopenharmony_ci	case 100:
488c2ecf20Sopenharmony_ci		data = (1 << (speed_offset + 16)) | (1 << speed_offset);
498c2ecf20Sopenharmony_ci		break;
508c2ecf20Sopenharmony_ci	default:
518c2ecf20Sopenharmony_ci		pr_err("speed %u not supported\n", speed);
528c2ecf20Sopenharmony_ci		return;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	err = regmap_write(emac->grf, emac->soc_data->grf_offset, data);
568c2ecf20Sopenharmony_ci	if (err)
578c2ecf20Sopenharmony_ci		pr_err("unable to apply speed %u to grf (%d)\n", speed, err);
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic const struct emac_rockchip_soc_data emac_rk3036_emac_data = {
618c2ecf20Sopenharmony_ci	.grf_offset = 0x140,   .grf_mode_offset = 8,
628c2ecf20Sopenharmony_ci	.grf_speed_offset = 9, .need_div_macclk = 1,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct emac_rockchip_soc_data emac_rk3066_emac_data = {
668c2ecf20Sopenharmony_ci	.grf_offset = 0x154,   .grf_mode_offset = 0,
678c2ecf20Sopenharmony_ci	.grf_speed_offset = 1, .need_div_macclk = 0,
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic const struct emac_rockchip_soc_data emac_rk3188_emac_data = {
718c2ecf20Sopenharmony_ci	.grf_offset = 0x0a4,   .grf_mode_offset = 0,
728c2ecf20Sopenharmony_ci	.grf_speed_offset = 1, .need_div_macclk = 0,
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic const struct of_device_id emac_rockchip_dt_ids[] = {
768c2ecf20Sopenharmony_ci	{
778c2ecf20Sopenharmony_ci		.compatible = "rockchip,rk3036-emac",
788c2ecf20Sopenharmony_ci		.data = &emac_rk3036_emac_data,
798c2ecf20Sopenharmony_ci	},
808c2ecf20Sopenharmony_ci	{
818c2ecf20Sopenharmony_ci		.compatible = "rockchip,rk3066-emac",
828c2ecf20Sopenharmony_ci		.data = &emac_rk3066_emac_data,
838c2ecf20Sopenharmony_ci	},
848c2ecf20Sopenharmony_ci	{
858c2ecf20Sopenharmony_ci		.compatible = "rockchip,rk3188-emac",
868c2ecf20Sopenharmony_ci		.data = &emac_rk3188_emac_data,
878c2ecf20Sopenharmony_ci	},
888c2ecf20Sopenharmony_ci	{ /* Sentinel */ }
898c2ecf20Sopenharmony_ci};
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, emac_rockchip_dt_ids);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int emac_rockchip_probe(struct platform_device *pdev)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
968c2ecf20Sopenharmony_ci	struct net_device *ndev;
978c2ecf20Sopenharmony_ci	struct rockchip_priv_data *priv;
988c2ecf20Sopenharmony_ci	const struct of_device_id *match;
998c2ecf20Sopenharmony_ci	phy_interface_t interface;
1008c2ecf20Sopenharmony_ci	u32 data;
1018c2ecf20Sopenharmony_ci	int err;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	if (!pdev->dev.of_node)
1048c2ecf20Sopenharmony_ci		return -ENODEV;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	ndev = alloc_etherdev(sizeof(struct rockchip_priv_data));
1078c2ecf20Sopenharmony_ci	if (!ndev)
1088c2ecf20Sopenharmony_ci		return -ENOMEM;
1098c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, ndev);
1108c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(ndev, dev);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	priv = netdev_priv(ndev);
1138c2ecf20Sopenharmony_ci	priv->emac.drv_name = DRV_NAME;
1148c2ecf20Sopenharmony_ci	priv->emac.set_mac_speed = emac_rockchip_set_mac_speed;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	err = of_get_phy_mode(dev->of_node, &interface);
1178c2ecf20Sopenharmony_ci	if (err)
1188c2ecf20Sopenharmony_ci		goto out_netdev;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* RK3036/RK3066/RK3188 SoCs only support RMII */
1218c2ecf20Sopenharmony_ci	if (interface != PHY_INTERFACE_MODE_RMII) {
1228c2ecf20Sopenharmony_ci		dev_err(dev, "unsupported phy interface mode %d\n", interface);
1238c2ecf20Sopenharmony_ci		err = -ENOTSUPP;
1248c2ecf20Sopenharmony_ci		goto out_netdev;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
1288c2ecf20Sopenharmony_ci						    "rockchip,grf");
1298c2ecf20Sopenharmony_ci	if (IS_ERR(priv->grf)) {
1308c2ecf20Sopenharmony_ci		dev_err(dev, "failed to retrieve global register file (%ld)\n",
1318c2ecf20Sopenharmony_ci			PTR_ERR(priv->grf));
1328c2ecf20Sopenharmony_ci		err = PTR_ERR(priv->grf);
1338c2ecf20Sopenharmony_ci		goto out_netdev;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	match = of_match_node(emac_rockchip_dt_ids, dev->of_node);
1378c2ecf20Sopenharmony_ci	priv->soc_data = match->data;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	priv->emac.clk = devm_clk_get(dev, "hclk");
1408c2ecf20Sopenharmony_ci	if (IS_ERR(priv->emac.clk)) {
1418c2ecf20Sopenharmony_ci		dev_err(dev, "failed to retrieve host clock (%ld)\n",
1428c2ecf20Sopenharmony_ci			PTR_ERR(priv->emac.clk));
1438c2ecf20Sopenharmony_ci		err = PTR_ERR(priv->emac.clk);
1448c2ecf20Sopenharmony_ci		goto out_netdev;
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	priv->refclk = devm_clk_get(dev, "macref");
1488c2ecf20Sopenharmony_ci	if (IS_ERR(priv->refclk)) {
1498c2ecf20Sopenharmony_ci		dev_err(dev, "failed to retrieve reference clock (%ld)\n",
1508c2ecf20Sopenharmony_ci			PTR_ERR(priv->refclk));
1518c2ecf20Sopenharmony_ci		err = PTR_ERR(priv->refclk);
1528c2ecf20Sopenharmony_ci		goto out_netdev;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	err = clk_prepare_enable(priv->refclk);
1568c2ecf20Sopenharmony_ci	if (err) {
1578c2ecf20Sopenharmony_ci		dev_err(dev, "failed to enable reference clock (%d)\n", err);
1588c2ecf20Sopenharmony_ci		goto out_netdev;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	/* Optional regulator for PHY */
1628c2ecf20Sopenharmony_ci	priv->regulator = devm_regulator_get_optional(dev, "phy");
1638c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regulator)) {
1648c2ecf20Sopenharmony_ci		if (PTR_ERR(priv->regulator) == -EPROBE_DEFER) {
1658c2ecf20Sopenharmony_ci			err = -EPROBE_DEFER;
1668c2ecf20Sopenharmony_ci			goto out_clk_disable;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci		dev_err(dev, "no regulator found\n");
1698c2ecf20Sopenharmony_ci		priv->regulator = NULL;
1708c2ecf20Sopenharmony_ci	}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (priv->regulator) {
1738c2ecf20Sopenharmony_ci		err = regulator_enable(priv->regulator);
1748c2ecf20Sopenharmony_ci		if (err) {
1758c2ecf20Sopenharmony_ci			dev_err(dev, "failed to enable phy-supply (%d)\n", err);
1768c2ecf20Sopenharmony_ci			goto out_clk_disable;
1778c2ecf20Sopenharmony_ci		}
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* Set speed 100M */
1818c2ecf20Sopenharmony_ci	data = (1 << (priv->soc_data->grf_speed_offset + 16)) |
1828c2ecf20Sopenharmony_ci	       (1 << priv->soc_data->grf_speed_offset);
1838c2ecf20Sopenharmony_ci	/* Set RMII mode */
1848c2ecf20Sopenharmony_ci	data |= (1 << (priv->soc_data->grf_mode_offset + 16)) |
1858c2ecf20Sopenharmony_ci		(0 << priv->soc_data->grf_mode_offset);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	err = regmap_write(priv->grf, priv->soc_data->grf_offset, data);
1888c2ecf20Sopenharmony_ci	if (err) {
1898c2ecf20Sopenharmony_ci		dev_err(dev, "unable to apply initial settings to grf (%d)\n",
1908c2ecf20Sopenharmony_ci			err);
1918c2ecf20Sopenharmony_ci		goto out_regulator_disable;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	/* RMII interface needs always a rate of 50MHz */
1958c2ecf20Sopenharmony_ci	err = clk_set_rate(priv->refclk, 50000000);
1968c2ecf20Sopenharmony_ci	if (err) {
1978c2ecf20Sopenharmony_ci		dev_err(dev,
1988c2ecf20Sopenharmony_ci			"failed to change reference clock rate (%d)\n", err);
1998c2ecf20Sopenharmony_ci		goto out_regulator_disable;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (priv->soc_data->need_div_macclk) {
2038c2ecf20Sopenharmony_ci		priv->macclk = devm_clk_get(dev, "macclk");
2048c2ecf20Sopenharmony_ci		if (IS_ERR(priv->macclk)) {
2058c2ecf20Sopenharmony_ci			dev_err(dev, "failed to retrieve mac clock (%ld)\n",
2068c2ecf20Sopenharmony_ci				PTR_ERR(priv->macclk));
2078c2ecf20Sopenharmony_ci			err = PTR_ERR(priv->macclk);
2088c2ecf20Sopenharmony_ci			goto out_regulator_disable;
2098c2ecf20Sopenharmony_ci		}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci		err = clk_prepare_enable(priv->macclk);
2128c2ecf20Sopenharmony_ci		if (err) {
2138c2ecf20Sopenharmony_ci			dev_err(dev, "failed to enable mac clock (%d)\n", err);
2148c2ecf20Sopenharmony_ci			goto out_regulator_disable;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci		/* RMII TX/RX needs always a rate of 25MHz */
2188c2ecf20Sopenharmony_ci		err = clk_set_rate(priv->macclk, 25000000);
2198c2ecf20Sopenharmony_ci		if (err) {
2208c2ecf20Sopenharmony_ci			dev_err(dev,
2218c2ecf20Sopenharmony_ci				"failed to change mac clock rate (%d)\n", err);
2228c2ecf20Sopenharmony_ci			goto out_clk_disable_macclk;
2238c2ecf20Sopenharmony_ci		}
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	err = arc_emac_probe(ndev, interface);
2278c2ecf20Sopenharmony_ci	if (err) {
2288c2ecf20Sopenharmony_ci		dev_err(dev, "failed to probe arc emac (%d)\n", err);
2298c2ecf20Sopenharmony_ci		goto out_clk_disable_macclk;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return 0;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ciout_clk_disable_macclk:
2358c2ecf20Sopenharmony_ci	if (priv->soc_data->need_div_macclk)
2368c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->macclk);
2378c2ecf20Sopenharmony_ciout_regulator_disable:
2388c2ecf20Sopenharmony_ci	if (priv->regulator)
2398c2ecf20Sopenharmony_ci		regulator_disable(priv->regulator);
2408c2ecf20Sopenharmony_ciout_clk_disable:
2418c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->refclk);
2428c2ecf20Sopenharmony_ciout_netdev:
2438c2ecf20Sopenharmony_ci	free_netdev(ndev);
2448c2ecf20Sopenharmony_ci	return err;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int emac_rockchip_remove(struct platform_device *pdev)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct net_device *ndev = platform_get_drvdata(pdev);
2508c2ecf20Sopenharmony_ci	struct rockchip_priv_data *priv = netdev_priv(ndev);
2518c2ecf20Sopenharmony_ci	int err;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	err = arc_emac_remove(ndev);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->refclk);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (priv->regulator)
2588c2ecf20Sopenharmony_ci		regulator_disable(priv->regulator);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (priv->soc_data->need_div_macclk)
2618c2ecf20Sopenharmony_ci		clk_disable_unprepare(priv->macclk);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	free_netdev(ndev);
2648c2ecf20Sopenharmony_ci	return err;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic struct platform_driver emac_rockchip_driver = {
2688c2ecf20Sopenharmony_ci	.probe = emac_rockchip_probe,
2698c2ecf20Sopenharmony_ci	.remove = emac_rockchip_remove,
2708c2ecf20Sopenharmony_ci	.driver = {
2718c2ecf20Sopenharmony_ci		.name = DRV_NAME,
2728c2ecf20Sopenharmony_ci		.of_match_table  = emac_rockchip_dt_ids,
2738c2ecf20Sopenharmony_ci	},
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cimodule_platform_driver(emac_rockchip_driver);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ciMODULE_AUTHOR("Romain Perier <romain.perier@gmail.com>");
2798c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Rockchip EMAC platform driver");
2808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
281