187 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer4 *5 * Copyright (C) 2013 Chen-Yu Tsai6 *7 * Chen-Yu Tsai <wens@csie.org>8 */9 10#include <linux/stmmac.h>11#include <linux/clk.h>12#include <linux/module.h>13#include <linux/phy.h>14#include <linux/platform_device.h>15#include <linux/of_net.h>16#include <linux/regulator/consumer.h>17 18#include "stmmac_platform.h"19 20struct sunxi_priv_data {21 phy_interface_t interface;22 int clk_enabled;23 struct clk *tx_clk;24 struct regulator *regulator;25};26 27#define SUN7I_GMAC_GMII_RGMII_RATE 12500000028#define SUN7I_GMAC_MII_RATE 2500000029 30static int sun7i_gmac_init(struct platform_device *pdev, void *priv)31{32 struct sunxi_priv_data *gmac = priv;33 int ret = 0;34 35 if (gmac->regulator) {36 ret = regulator_enable(gmac->regulator);37 if (ret)38 return ret;39 }40 41 /* Set GMAC interface port mode42 *43 * The GMAC TX clock lines are configured by setting the clock44 * rate, which then uses the auto-reparenting feature of the45 * clock driver, and enabling/disabling the clock.46 */47 if (phy_interface_mode_is_rgmii(gmac->interface)) {48 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);49 clk_prepare_enable(gmac->tx_clk);50 gmac->clk_enabled = 1;51 } else {52 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);53 ret = clk_prepare(gmac->tx_clk);54 if (ret && gmac->regulator)55 regulator_disable(gmac->regulator);56 }57 58 return ret;59}60 61static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)62{63 struct sunxi_priv_data *gmac = priv;64 65 if (gmac->clk_enabled) {66 clk_disable(gmac->tx_clk);67 gmac->clk_enabled = 0;68 }69 clk_unprepare(gmac->tx_clk);70 71 if (gmac->regulator)72 regulator_disable(gmac->regulator);73}74 75static void sun7i_fix_speed(void *priv, unsigned int speed, unsigned int mode)76{77 struct sunxi_priv_data *gmac = priv;78 79 /* only GMII mode requires us to reconfigure the clock lines */80 if (gmac->interface != PHY_INTERFACE_MODE_GMII)81 return;82 83 if (gmac->clk_enabled) {84 clk_disable(gmac->tx_clk);85 gmac->clk_enabled = 0;86 }87 clk_unprepare(gmac->tx_clk);88 89 if (speed == 1000) {90 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);91 clk_prepare_enable(gmac->tx_clk);92 gmac->clk_enabled = 1;93 } else {94 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);95 clk_prepare(gmac->tx_clk);96 }97}98 99static int sun7i_gmac_probe(struct platform_device *pdev)100{101 struct plat_stmmacenet_data *plat_dat;102 struct stmmac_resources stmmac_res;103 struct sunxi_priv_data *gmac;104 struct device *dev = &pdev->dev;105 int ret;106 107 ret = stmmac_get_platform_resources(pdev, &stmmac_res);108 if (ret)109 return ret;110 111 plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac);112 if (IS_ERR(plat_dat))113 return PTR_ERR(plat_dat);114 115 gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);116 if (!gmac)117 return -ENOMEM;118 119 ret = of_get_phy_mode(dev->of_node, &gmac->interface);120 if (ret && ret != -ENODEV) {121 dev_err(dev, "Can't get phy-mode\n");122 return ret;123 }124 125 gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");126 if (IS_ERR(gmac->tx_clk)) {127 dev_err(dev, "could not get tx clock\n");128 return PTR_ERR(gmac->tx_clk);129 }130 131 /* Optional regulator for PHY */132 gmac->regulator = devm_regulator_get_optional(dev, "phy");133 if (IS_ERR(gmac->regulator)) {134 if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER)135 return -EPROBE_DEFER;136 dev_info(dev, "no regulator found\n");137 gmac->regulator = NULL;138 }139 140 /* platform data specifying hardware features and callbacks.141 * hardware features were copied from Allwinner drivers. */142 plat_dat->tx_coe = 1;143 plat_dat->has_gmac = true;144 plat_dat->bsp_priv = gmac;145 plat_dat->init = sun7i_gmac_init;146 plat_dat->exit = sun7i_gmac_exit;147 plat_dat->fix_mac_speed = sun7i_fix_speed;148 plat_dat->tx_fifo_size = 4096;149 plat_dat->rx_fifo_size = 16384;150 151 ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv);152 if (ret)153 return ret;154 155 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);156 if (ret)157 goto err_gmac_exit;158 159 return 0;160 161err_gmac_exit:162 sun7i_gmac_exit(pdev, plat_dat->bsp_priv);163 164 return ret;165}166 167static const struct of_device_id sun7i_dwmac_match[] = {168 { .compatible = "allwinner,sun7i-a20-gmac" },169 { }170};171MODULE_DEVICE_TABLE(of, sun7i_dwmac_match);172 173static struct platform_driver sun7i_dwmac_driver = {174 .probe = sun7i_gmac_probe,175 .remove_new = stmmac_pltfr_remove,176 .driver = {177 .name = "sun7i-dwmac",178 .pm = &stmmac_pltfr_pm_ops,179 .of_match_table = sun7i_dwmac_match,180 },181};182module_platform_driver(sun7i_dwmac_driver);183 184MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");185MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer");186MODULE_LICENSE("GPL");187