162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * emac-rockchip.c - Rockchip EMAC specific glue layer 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Romain Perier <romain.perier@gmail.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of_net.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/regmap.h> 1462306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "emac.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define DRV_NAME "rockchip_emac" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct emac_rockchip_soc_data { 2162306a36Sopenharmony_ci unsigned int grf_offset; 2262306a36Sopenharmony_ci unsigned int grf_mode_offset; 2362306a36Sopenharmony_ci unsigned int grf_speed_offset; 2462306a36Sopenharmony_ci bool need_div_macclk; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct rockchip_priv_data { 2862306a36Sopenharmony_ci struct arc_emac_priv emac; 2962306a36Sopenharmony_ci struct regmap *grf; 3062306a36Sopenharmony_ci const struct emac_rockchip_soc_data *soc_data; 3162306a36Sopenharmony_ci struct regulator *regulator; 3262306a36Sopenharmony_ci struct clk *refclk; 3362306a36Sopenharmony_ci struct clk *macclk; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic void emac_rockchip_set_mac_speed(void *priv, unsigned int speed) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct rockchip_priv_data *emac = priv; 3962306a36Sopenharmony_ci u32 speed_offset = emac->soc_data->grf_speed_offset; 4062306a36Sopenharmony_ci u32 data; 4162306a36Sopenharmony_ci int err = 0; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci switch (speed) { 4462306a36Sopenharmony_ci case 10: 4562306a36Sopenharmony_ci data = (1 << (speed_offset + 16)) | (0 << speed_offset); 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci case 100: 4862306a36Sopenharmony_ci data = (1 << (speed_offset + 16)) | (1 << speed_offset); 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci default: 5162306a36Sopenharmony_ci pr_err("speed %u not supported\n", speed); 5262306a36Sopenharmony_ci return; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci err = regmap_write(emac->grf, emac->soc_data->grf_offset, data); 5662306a36Sopenharmony_ci if (err) 5762306a36Sopenharmony_ci pr_err("unable to apply speed %u to grf (%d)\n", speed, err); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic const struct emac_rockchip_soc_data emac_rk3036_emac_data = { 6162306a36Sopenharmony_ci .grf_offset = 0x140, .grf_mode_offset = 8, 6262306a36Sopenharmony_ci .grf_speed_offset = 9, .need_div_macclk = 1, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic const struct emac_rockchip_soc_data emac_rk3066_emac_data = { 6662306a36Sopenharmony_ci .grf_offset = 0x154, .grf_mode_offset = 0, 6762306a36Sopenharmony_ci .grf_speed_offset = 1, .need_div_macclk = 0, 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const struct emac_rockchip_soc_data emac_rk3188_emac_data = { 7162306a36Sopenharmony_ci .grf_offset = 0x0a4, .grf_mode_offset = 0, 7262306a36Sopenharmony_ci .grf_speed_offset = 1, .need_div_macclk = 0, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const struct of_device_id emac_rockchip_dt_ids[] = { 7662306a36Sopenharmony_ci { 7762306a36Sopenharmony_ci .compatible = "rockchip,rk3036-emac", 7862306a36Sopenharmony_ci .data = &emac_rk3036_emac_data, 7962306a36Sopenharmony_ci }, 8062306a36Sopenharmony_ci { 8162306a36Sopenharmony_ci .compatible = "rockchip,rk3066-emac", 8262306a36Sopenharmony_ci .data = &emac_rk3066_emac_data, 8362306a36Sopenharmony_ci }, 8462306a36Sopenharmony_ci { 8562306a36Sopenharmony_ci .compatible = "rockchip,rk3188-emac", 8662306a36Sopenharmony_ci .data = &emac_rk3188_emac_data, 8762306a36Sopenharmony_ci }, 8862306a36Sopenharmony_ci { /* Sentinel */ } 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, emac_rockchip_dt_ids); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int emac_rockchip_probe(struct platform_device *pdev) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 9662306a36Sopenharmony_ci struct net_device *ndev; 9762306a36Sopenharmony_ci struct rockchip_priv_data *priv; 9862306a36Sopenharmony_ci const struct of_device_id *match; 9962306a36Sopenharmony_ci phy_interface_t interface; 10062306a36Sopenharmony_ci u32 data; 10162306a36Sopenharmony_ci int err; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (!pdev->dev.of_node) 10462306a36Sopenharmony_ci return -ENODEV; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct rockchip_priv_data)); 10762306a36Sopenharmony_ci if (!ndev) 10862306a36Sopenharmony_ci return -ENOMEM; 10962306a36Sopenharmony_ci platform_set_drvdata(pdev, ndev); 11062306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, dev); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci priv = netdev_priv(ndev); 11362306a36Sopenharmony_ci priv->emac.drv_name = DRV_NAME; 11462306a36Sopenharmony_ci priv->emac.set_mac_speed = emac_rockchip_set_mac_speed; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci err = of_get_phy_mode(dev->of_node, &interface); 11762306a36Sopenharmony_ci if (err) 11862306a36Sopenharmony_ci goto out_netdev; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* RK3036/RK3066/RK3188 SoCs only support RMII */ 12162306a36Sopenharmony_ci if (interface != PHY_INTERFACE_MODE_RMII) { 12262306a36Sopenharmony_ci dev_err(dev, "unsupported phy interface mode %d\n", interface); 12362306a36Sopenharmony_ci err = -ENOTSUPP; 12462306a36Sopenharmony_ci goto out_netdev; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, 12862306a36Sopenharmony_ci "rockchip,grf"); 12962306a36Sopenharmony_ci if (IS_ERR(priv->grf)) { 13062306a36Sopenharmony_ci dev_err(dev, "failed to retrieve global register file (%ld)\n", 13162306a36Sopenharmony_ci PTR_ERR(priv->grf)); 13262306a36Sopenharmony_ci err = PTR_ERR(priv->grf); 13362306a36Sopenharmony_ci goto out_netdev; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci match = of_match_node(emac_rockchip_dt_ids, dev->of_node); 13762306a36Sopenharmony_ci priv->soc_data = match->data; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci priv->emac.clk = devm_clk_get(dev, "hclk"); 14062306a36Sopenharmony_ci if (IS_ERR(priv->emac.clk)) { 14162306a36Sopenharmony_ci dev_err(dev, "failed to retrieve host clock (%ld)\n", 14262306a36Sopenharmony_ci PTR_ERR(priv->emac.clk)); 14362306a36Sopenharmony_ci err = PTR_ERR(priv->emac.clk); 14462306a36Sopenharmony_ci goto out_netdev; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci priv->refclk = devm_clk_get(dev, "macref"); 14862306a36Sopenharmony_ci if (IS_ERR(priv->refclk)) { 14962306a36Sopenharmony_ci dev_err(dev, "failed to retrieve reference clock (%ld)\n", 15062306a36Sopenharmony_ci PTR_ERR(priv->refclk)); 15162306a36Sopenharmony_ci err = PTR_ERR(priv->refclk); 15262306a36Sopenharmony_ci goto out_netdev; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci err = clk_prepare_enable(priv->refclk); 15662306a36Sopenharmony_ci if (err) { 15762306a36Sopenharmony_ci dev_err(dev, "failed to enable reference clock (%d)\n", err); 15862306a36Sopenharmony_ci goto out_netdev; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Optional regulator for PHY */ 16262306a36Sopenharmony_ci priv->regulator = devm_regulator_get_optional(dev, "phy"); 16362306a36Sopenharmony_ci if (IS_ERR(priv->regulator)) { 16462306a36Sopenharmony_ci if (PTR_ERR(priv->regulator) == -EPROBE_DEFER) { 16562306a36Sopenharmony_ci err = -EPROBE_DEFER; 16662306a36Sopenharmony_ci goto out_clk_disable; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci dev_err(dev, "no regulator found\n"); 16962306a36Sopenharmony_ci priv->regulator = NULL; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (priv->regulator) { 17362306a36Sopenharmony_ci err = regulator_enable(priv->regulator); 17462306a36Sopenharmony_ci if (err) { 17562306a36Sopenharmony_ci dev_err(dev, "failed to enable phy-supply (%d)\n", err); 17662306a36Sopenharmony_ci goto out_clk_disable; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Set speed 100M */ 18162306a36Sopenharmony_ci data = (1 << (priv->soc_data->grf_speed_offset + 16)) | 18262306a36Sopenharmony_ci (1 << priv->soc_data->grf_speed_offset); 18362306a36Sopenharmony_ci /* Set RMII mode */ 18462306a36Sopenharmony_ci data |= (1 << (priv->soc_data->grf_mode_offset + 16)) | 18562306a36Sopenharmony_ci (0 << priv->soc_data->grf_mode_offset); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci err = regmap_write(priv->grf, priv->soc_data->grf_offset, data); 18862306a36Sopenharmony_ci if (err) { 18962306a36Sopenharmony_ci dev_err(dev, "unable to apply initial settings to grf (%d)\n", 19062306a36Sopenharmony_ci err); 19162306a36Sopenharmony_ci goto out_regulator_disable; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* RMII interface needs always a rate of 50MHz */ 19562306a36Sopenharmony_ci err = clk_set_rate(priv->refclk, 50000000); 19662306a36Sopenharmony_ci if (err) { 19762306a36Sopenharmony_ci dev_err(dev, 19862306a36Sopenharmony_ci "failed to change reference clock rate (%d)\n", err); 19962306a36Sopenharmony_ci goto out_regulator_disable; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (priv->soc_data->need_div_macclk) { 20362306a36Sopenharmony_ci priv->macclk = devm_clk_get(dev, "macclk"); 20462306a36Sopenharmony_ci if (IS_ERR(priv->macclk)) { 20562306a36Sopenharmony_ci dev_err(dev, "failed to retrieve mac clock (%ld)\n", 20662306a36Sopenharmony_ci PTR_ERR(priv->macclk)); 20762306a36Sopenharmony_ci err = PTR_ERR(priv->macclk); 20862306a36Sopenharmony_ci goto out_regulator_disable; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci err = clk_prepare_enable(priv->macclk); 21262306a36Sopenharmony_ci if (err) { 21362306a36Sopenharmony_ci dev_err(dev, "failed to enable mac clock (%d)\n", err); 21462306a36Sopenharmony_ci goto out_regulator_disable; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* RMII TX/RX needs always a rate of 25MHz */ 21862306a36Sopenharmony_ci err = clk_set_rate(priv->macclk, 25000000); 21962306a36Sopenharmony_ci if (err) { 22062306a36Sopenharmony_ci dev_err(dev, 22162306a36Sopenharmony_ci "failed to change mac clock rate (%d)\n", err); 22262306a36Sopenharmony_ci goto out_clk_disable_macclk; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci err = arc_emac_probe(ndev, interface); 22762306a36Sopenharmony_ci if (err) { 22862306a36Sopenharmony_ci dev_err(dev, "failed to probe arc emac (%d)\n", err); 22962306a36Sopenharmony_ci goto out_clk_disable_macclk; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ciout_clk_disable_macclk: 23562306a36Sopenharmony_ci if (priv->soc_data->need_div_macclk) 23662306a36Sopenharmony_ci clk_disable_unprepare(priv->macclk); 23762306a36Sopenharmony_ciout_regulator_disable: 23862306a36Sopenharmony_ci if (priv->regulator) 23962306a36Sopenharmony_ci regulator_disable(priv->regulator); 24062306a36Sopenharmony_ciout_clk_disable: 24162306a36Sopenharmony_ci clk_disable_unprepare(priv->refclk); 24262306a36Sopenharmony_ciout_netdev: 24362306a36Sopenharmony_ci free_netdev(ndev); 24462306a36Sopenharmony_ci return err; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int emac_rockchip_remove(struct platform_device *pdev) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(pdev); 25062306a36Sopenharmony_ci struct rockchip_priv_data *priv = netdev_priv(ndev); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci arc_emac_remove(ndev); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci clk_disable_unprepare(priv->refclk); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (priv->regulator) 25762306a36Sopenharmony_ci regulator_disable(priv->regulator); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (priv->soc_data->need_div_macclk) 26062306a36Sopenharmony_ci clk_disable_unprepare(priv->macclk); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci free_netdev(ndev); 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic struct platform_driver emac_rockchip_driver = { 26762306a36Sopenharmony_ci .probe = emac_rockchip_probe, 26862306a36Sopenharmony_ci .remove = emac_rockchip_remove, 26962306a36Sopenharmony_ci .driver = { 27062306a36Sopenharmony_ci .name = DRV_NAME, 27162306a36Sopenharmony_ci .of_match_table = emac_rockchip_dt_ids, 27262306a36Sopenharmony_ci }, 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cimodule_platform_driver(emac_rockchip_driver); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ciMODULE_AUTHOR("Romain Perier <romain.perier@gmail.com>"); 27862306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip EMAC platform driver"); 27962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 280