1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Oxford Semiconductor OXNAS DWMAC glue layer 4 * 5 * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com> 6 * Copyright (C) 2014 Daniel Golle <daniel@makrotopia.org> 7 * Copyright (C) 2013 Ma Haijun <mahaijuns@gmail.com> 8 * Copyright (C) 2012 John Crispin <blogic@openwrt.org> 9 */ 10 11#include <linux/device.h> 12#include <linux/io.h> 13#include <linux/module.h> 14#include <linux/of.h> 15#include <linux/platform_device.h> 16#include <linux/regmap.h> 17#include <linux/mfd/syscon.h> 18#include <linux/stmmac.h> 19 20#include "stmmac_platform.h" 21 22/* System Control regmap offsets */ 23#define OXNAS_DWMAC_CTRL_REGOFFSET 0x78 24#define OXNAS_DWMAC_DELAY_REGOFFSET 0x100 25 26/* Control Register */ 27#define DWMAC_CKEN_RX_IN 14 28#define DWMAC_CKEN_RXN_OUT 13 29#define DWMAC_CKEN_RX_OUT 12 30#define DWMAC_CKEN_TX_IN 10 31#define DWMAC_CKEN_TXN_OUT 9 32#define DWMAC_CKEN_TX_OUT 8 33#define DWMAC_RX_SOURCE 7 34#define DWMAC_TX_SOURCE 6 35#define DWMAC_LOW_TX_SOURCE 4 36#define DWMAC_AUTO_TX_SOURCE 3 37#define DWMAC_RGMII 2 38#define DWMAC_SIMPLE_MUX 1 39#define DWMAC_CKEN_GTX 0 40 41/* Delay register */ 42#define DWMAC_TX_VARDELAY_SHIFT 0 43#define DWMAC_TXN_VARDELAY_SHIFT 8 44#define DWMAC_RX_VARDELAY_SHIFT 16 45#define DWMAC_RXN_VARDELAY_SHIFT 24 46#define DWMAC_TX_VARDELAY(d) ((d) << DWMAC_TX_VARDELAY_SHIFT) 47#define DWMAC_TXN_VARDELAY(d) ((d) << DWMAC_TXN_VARDELAY_SHIFT) 48#define DWMAC_RX_VARDELAY(d) ((d) << DWMAC_RX_VARDELAY_SHIFT) 49#define DWMAC_RXN_VARDELAY(d) ((d) << DWMAC_RXN_VARDELAY_SHIFT) 50 51struct oxnas_dwmac { 52 struct device *dev; 53 struct clk *clk; 54 struct regmap *regmap; 55}; 56 57static int oxnas_dwmac_init(struct platform_device *pdev, void *priv) 58{ 59 struct oxnas_dwmac *dwmac = priv; 60 unsigned int value; 61 int ret; 62 63 /* Reset HW here before changing the glue configuration */ 64 ret = device_reset(dwmac->dev); 65 if (ret) 66 return ret; 67 68 ret = clk_prepare_enable(dwmac->clk); 69 if (ret) 70 return ret; 71 72 ret = regmap_read(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, &value); 73 if (ret < 0) { 74 clk_disable_unprepare(dwmac->clk); 75 return ret; 76 } 77 78 /* Enable GMII_GTXCLK to follow GMII_REFCLK, required for gigabit PHY */ 79 value |= BIT(DWMAC_CKEN_GTX) | 80 /* Use simple mux for 25/125 Mhz clock switching */ 81 BIT(DWMAC_SIMPLE_MUX) | 82 /* set auto switch tx clock source */ 83 BIT(DWMAC_AUTO_TX_SOURCE) | 84 /* enable tx & rx vardelay */ 85 BIT(DWMAC_CKEN_TX_OUT) | 86 BIT(DWMAC_CKEN_TXN_OUT) | 87 BIT(DWMAC_CKEN_TX_IN) | 88 BIT(DWMAC_CKEN_RX_OUT) | 89 BIT(DWMAC_CKEN_RXN_OUT) | 90 BIT(DWMAC_CKEN_RX_IN); 91 regmap_write(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, value); 92 93 /* set tx & rx vardelay */ 94 value = DWMAC_TX_VARDELAY(4) | 95 DWMAC_TXN_VARDELAY(2) | 96 DWMAC_RX_VARDELAY(10) | 97 DWMAC_RXN_VARDELAY(8); 98 regmap_write(dwmac->regmap, OXNAS_DWMAC_DELAY_REGOFFSET, value); 99 100 return 0; 101} 102 103static void oxnas_dwmac_exit(struct platform_device *pdev, void *priv) 104{ 105 struct oxnas_dwmac *dwmac = priv; 106 107 clk_disable_unprepare(dwmac->clk); 108} 109 110static int oxnas_dwmac_probe(struct platform_device *pdev) 111{ 112 struct plat_stmmacenet_data *plat_dat; 113 struct stmmac_resources stmmac_res; 114 struct oxnas_dwmac *dwmac; 115 int ret; 116 117 ret = stmmac_get_platform_resources(pdev, &stmmac_res); 118 if (ret) 119 return ret; 120 121 plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); 122 if (IS_ERR(plat_dat)) 123 return PTR_ERR(plat_dat); 124 125 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 126 if (!dwmac) { 127 ret = -ENOMEM; 128 goto err_remove_config_dt; 129 } 130 131 dwmac->dev = &pdev->dev; 132 plat_dat->bsp_priv = dwmac; 133 plat_dat->init = oxnas_dwmac_init; 134 plat_dat->exit = oxnas_dwmac_exit; 135 136 dwmac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 137 "oxsemi,sys-ctrl"); 138 if (IS_ERR(dwmac->regmap)) { 139 dev_err(&pdev->dev, "failed to have sysctrl regmap\n"); 140 ret = PTR_ERR(dwmac->regmap); 141 goto err_remove_config_dt; 142 } 143 144 dwmac->clk = devm_clk_get(&pdev->dev, "gmac"); 145 if (IS_ERR(dwmac->clk)) { 146 ret = PTR_ERR(dwmac->clk); 147 goto err_remove_config_dt; 148 } 149 150 ret = oxnas_dwmac_init(pdev, plat_dat->bsp_priv); 151 if (ret) 152 goto err_remove_config_dt; 153 154 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); 155 if (ret) 156 goto err_dwmac_exit; 157 158 159 return 0; 160 161err_dwmac_exit: 162 oxnas_dwmac_exit(pdev, plat_dat->bsp_priv); 163err_remove_config_dt: 164 stmmac_remove_config_dt(pdev, plat_dat); 165 166 return ret; 167} 168 169static const struct of_device_id oxnas_dwmac_match[] = { 170 { .compatible = "oxsemi,ox820-dwmac" }, 171 { } 172}; 173MODULE_DEVICE_TABLE(of, oxnas_dwmac_match); 174 175static struct platform_driver oxnas_dwmac_driver = { 176 .probe = oxnas_dwmac_probe, 177 .remove = stmmac_pltfr_remove, 178 .driver = { 179 .name = "oxnas-dwmac", 180 .pm = &stmmac_pltfr_pm_ops, 181 .of_match_table = oxnas_dwmac_match, 182 }, 183}; 184module_platform_driver(oxnas_dwmac_driver); 185 186MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 187MODULE_DESCRIPTION("Oxford Semiconductor OXNAS DWMAC glue layer"); 188MODULE_LICENSE("GPL v2"); 189