1 /** 2 * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer 3 * 4 * Copyright (C) 2013 Chen-Yu Tsai 5 * 6 * Chen-Yu Tsai <wens@csie.org> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19 #include <linux/stmmac.h> 20 #include <linux/clk.h> 21 #include <linux/phy.h> 22 #include <linux/of_net.h> 23 #include <linux/regulator/consumer.h> 24 25 struct sunxi_priv_data { 26 int interface; 27 int clk_enabled; 28 struct clk *tx_clk; 29 struct regulator *regulator; 30 }; 31 32 static void *sun7i_gmac_setup(struct platform_device *pdev) 33 { 34 struct sunxi_priv_data *gmac; 35 struct device *dev = &pdev->dev; 36 37 gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL); 38 if (!gmac) 39 return ERR_PTR(-ENOMEM); 40 41 gmac->interface = of_get_phy_mode(dev->of_node); 42 43 gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx"); 44 if (IS_ERR(gmac->tx_clk)) { 45 dev_err(dev, "could not get tx clock\n"); 46 return gmac->tx_clk; 47 } 48 49 /* Optional regulator for PHY */ 50 gmac->regulator = devm_regulator_get_optional(dev, "phy"); 51 if (IS_ERR(gmac->regulator)) { 52 if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) 53 return ERR_PTR(-EPROBE_DEFER); 54 dev_info(dev, "no regulator found\n"); 55 gmac->regulator = NULL; 56 } 57 58 return gmac; 59 } 60 61 #define SUN7I_GMAC_GMII_RGMII_RATE 125000000 62 #define SUN7I_GMAC_MII_RATE 25000000 63 64 static int sun7i_gmac_init(struct platform_device *pdev, void *priv) 65 { 66 struct sunxi_priv_data *gmac = priv; 67 int ret; 68 69 if (gmac->regulator) { 70 ret = regulator_enable(gmac->regulator); 71 if (ret) 72 return ret; 73 } 74 75 /* Set GMAC interface port mode 76 * 77 * The GMAC TX clock lines are configured by setting the clock 78 * rate, which then uses the auto-reparenting feature of the 79 * clock driver, and enabling/disabling the clock. 80 */ 81 if (gmac->interface == PHY_INTERFACE_MODE_RGMII) { 82 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE); 83 clk_prepare_enable(gmac->tx_clk); 84 gmac->clk_enabled = 1; 85 } else { 86 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE); 87 clk_prepare(gmac->tx_clk); 88 } 89 90 return 0; 91 } 92 93 static void sun7i_gmac_exit(struct platform_device *pdev, void *priv) 94 { 95 struct sunxi_priv_data *gmac = priv; 96 97 if (gmac->clk_enabled) { 98 clk_disable(gmac->tx_clk); 99 gmac->clk_enabled = 0; 100 } 101 clk_unprepare(gmac->tx_clk); 102 103 if (gmac->regulator) 104 regulator_disable(gmac->regulator); 105 } 106 107 static void sun7i_fix_speed(void *priv, unsigned int speed) 108 { 109 struct sunxi_priv_data *gmac = priv; 110 111 /* only GMII mode requires us to reconfigure the clock lines */ 112 if (gmac->interface != PHY_INTERFACE_MODE_GMII) 113 return; 114 115 if (gmac->clk_enabled) { 116 clk_disable(gmac->tx_clk); 117 gmac->clk_enabled = 0; 118 } 119 clk_unprepare(gmac->tx_clk); 120 121 if (speed == 1000) { 122 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE); 123 clk_prepare_enable(gmac->tx_clk); 124 gmac->clk_enabled = 1; 125 } else { 126 clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE); 127 clk_prepare(gmac->tx_clk); 128 } 129 } 130 131 /* of_data specifying hardware features and callbacks. 132 * hardware features were copied from Allwinner drivers. */ 133 const struct stmmac_of_data sun7i_gmac_data = { 134 .has_gmac = 1, 135 .tx_coe = 1, 136 .fix_mac_speed = sun7i_fix_speed, 137 .setup = sun7i_gmac_setup, 138 .init = sun7i_gmac_init, 139 .exit = sun7i_gmac_exit, 140 }; 141