1 /* Copyright Altera Corporation (C) 2014. All rights reserved. 2 * 3 * This program is free software; you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License, version 2, 5 * as published by the Free Software Foundation. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program. If not, see <http://www.gnu.org/licenses/>. 14 * 15 * Adopted from dwmac-sti.c 16 */ 17 18 #include <linux/mfd/syscon.h> 19 #include <linux/of.h> 20 #include <linux/of_net.h> 21 #include <linux/phy.h> 22 #include <linux/regmap.h> 23 #include <linux/stmmac.h> 24 25 #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 26 #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 27 #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2 28 #define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2 29 #define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003 30 31 struct socfpga_dwmac { 32 int interface; 33 u32 reg_offset; 34 u32 reg_shift; 35 struct device *dev; 36 struct regmap *sys_mgr_base_addr; 37 }; 38 39 static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev) 40 { 41 struct device_node *np = dev->of_node; 42 struct regmap *sys_mgr_base_addr; 43 u32 reg_offset, reg_shift; 44 int ret; 45 46 dwmac->interface = of_get_phy_mode(np); 47 48 sys_mgr_base_addr = syscon_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon"); 49 if (IS_ERR(sys_mgr_base_addr)) { 50 dev_info(dev, "No sysmgr-syscon node found\n"); 51 return PTR_ERR(sys_mgr_base_addr); 52 } 53 54 ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, ®_offset); 55 if (ret) { 56 dev_info(dev, "Could not read reg_offset from sysmgr-syscon!\n"); 57 return -EINVAL; 58 } 59 60 ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, ®_shift); 61 if (ret) { 62 dev_info(dev, "Could not read reg_shift from sysmgr-syscon!\n"); 63 return -EINVAL; 64 } 65 66 dwmac->reg_offset = reg_offset; 67 dwmac->reg_shift = reg_shift; 68 dwmac->sys_mgr_base_addr = sys_mgr_base_addr; 69 dwmac->dev = dev; 70 71 return 0; 72 } 73 74 static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac) 75 { 76 struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr; 77 int phymode = dwmac->interface; 78 u32 reg_offset = dwmac->reg_offset; 79 u32 reg_shift = dwmac->reg_shift; 80 u32 ctrl, val; 81 82 switch (phymode) { 83 case PHY_INTERFACE_MODE_RGMII: 84 val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII; 85 break; 86 case PHY_INTERFACE_MODE_MII: 87 case PHY_INTERFACE_MODE_GMII: 88 val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; 89 break; 90 default: 91 dev_err(dwmac->dev, "bad phy mode %d\n", phymode); 92 return -EINVAL; 93 } 94 95 regmap_read(sys_mgr_base_addr, reg_offset, &ctrl); 96 ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift); 97 ctrl |= val << reg_shift; 98 99 regmap_write(sys_mgr_base_addr, reg_offset, ctrl); 100 return 0; 101 } 102 103 static void *socfpga_dwmac_probe(struct platform_device *pdev) 104 { 105 struct device *dev = &pdev->dev; 106 int ret; 107 struct socfpga_dwmac *dwmac; 108 109 dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL); 110 if (!dwmac) 111 return ERR_PTR(-ENOMEM); 112 113 ret = socfpga_dwmac_parse_data(dwmac, dev); 114 if (ret) { 115 dev_err(dev, "Unable to parse OF data\n"); 116 return ERR_PTR(ret); 117 } 118 119 ret = socfpga_dwmac_setup(dwmac); 120 if (ret) { 121 dev_err(dev, "couldn't setup SoC glue (%d)\n", ret); 122 return ERR_PTR(ret); 123 } 124 125 return dwmac; 126 } 127 128 const struct stmmac_of_data socfpga_gmac_data = { 129 .setup = socfpga_dwmac_probe, 130 }; 131