dwmac-oxnas.c (6184B)
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/of_device.h> 16#include <linux/platform_device.h> 17#include <linux/regmap.h> 18#include <linux/mfd/syscon.h> 19#include <linux/stmmac.h> 20 21#include "stmmac_platform.h" 22 23/* System Control regmap offsets */ 24#define OXNAS_DWMAC_CTRL_REGOFFSET 0x78 25#define OXNAS_DWMAC_DELAY_REGOFFSET 0x100 26 27/* Control Register */ 28#define DWMAC_CKEN_RX_IN 14 29#define DWMAC_CKEN_RXN_OUT 13 30#define DWMAC_CKEN_RX_OUT 12 31#define DWMAC_CKEN_TX_IN 10 32#define DWMAC_CKEN_TXN_OUT 9 33#define DWMAC_CKEN_TX_OUT 8 34#define DWMAC_RX_SOURCE 7 35#define DWMAC_TX_SOURCE 6 36#define DWMAC_LOW_TX_SOURCE 4 37#define DWMAC_AUTO_TX_SOURCE 3 38#define DWMAC_RGMII 2 39#define DWMAC_SIMPLE_MUX 1 40#define DWMAC_CKEN_GTX 0 41 42/* Delay register */ 43#define DWMAC_TX_VARDELAY_SHIFT 0 44#define DWMAC_TXN_VARDELAY_SHIFT 8 45#define DWMAC_RX_VARDELAY_SHIFT 16 46#define DWMAC_RXN_VARDELAY_SHIFT 24 47#define DWMAC_TX_VARDELAY(d) ((d) << DWMAC_TX_VARDELAY_SHIFT) 48#define DWMAC_TXN_VARDELAY(d) ((d) << DWMAC_TXN_VARDELAY_SHIFT) 49#define DWMAC_RX_VARDELAY(d) ((d) << DWMAC_RX_VARDELAY_SHIFT) 50#define DWMAC_RXN_VARDELAY(d) ((d) << DWMAC_RXN_VARDELAY_SHIFT) 51 52struct oxnas_dwmac; 53 54struct oxnas_dwmac_data { 55 int (*setup)(struct oxnas_dwmac *dwmac); 56}; 57 58struct oxnas_dwmac { 59 struct device *dev; 60 struct clk *clk; 61 struct regmap *regmap; 62 const struct oxnas_dwmac_data *data; 63}; 64 65static int oxnas_dwmac_setup_ox810se(struct oxnas_dwmac *dwmac) 66{ 67 unsigned int value; 68 int ret; 69 70 ret = regmap_read(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, &value); 71 if (ret < 0) 72 return ret; 73 74 /* Enable GMII_GTXCLK to follow GMII_REFCLK, required for gigabit PHY */ 75 value |= BIT(DWMAC_CKEN_GTX) | 76 /* Use simple mux for 25/125 Mhz clock switching */ 77 BIT(DWMAC_SIMPLE_MUX); 78 79 regmap_write(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, value); 80 81 return 0; 82} 83 84static int oxnas_dwmac_setup_ox820(struct oxnas_dwmac *dwmac) 85{ 86 unsigned int value; 87 int ret; 88 89 ret = regmap_read(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, &value); 90 if (ret < 0) 91 return ret; 92 93 /* Enable GMII_GTXCLK to follow GMII_REFCLK, required for gigabit PHY */ 94 value |= BIT(DWMAC_CKEN_GTX) | 95 /* Use simple mux for 25/125 Mhz clock switching */ 96 BIT(DWMAC_SIMPLE_MUX) | 97 /* set auto switch tx clock source */ 98 BIT(DWMAC_AUTO_TX_SOURCE) | 99 /* enable tx & rx vardelay */ 100 BIT(DWMAC_CKEN_TX_OUT) | 101 BIT(DWMAC_CKEN_TXN_OUT) | 102 BIT(DWMAC_CKEN_TX_IN) | 103 BIT(DWMAC_CKEN_RX_OUT) | 104 BIT(DWMAC_CKEN_RXN_OUT) | 105 BIT(DWMAC_CKEN_RX_IN); 106 regmap_write(dwmac->regmap, OXNAS_DWMAC_CTRL_REGOFFSET, value); 107 108 /* set tx & rx vardelay */ 109 value = DWMAC_TX_VARDELAY(4) | 110 DWMAC_TXN_VARDELAY(2) | 111 DWMAC_RX_VARDELAY(10) | 112 DWMAC_RXN_VARDELAY(8); 113 regmap_write(dwmac->regmap, OXNAS_DWMAC_DELAY_REGOFFSET, value); 114 115 return 0; 116} 117 118static int oxnas_dwmac_init(struct platform_device *pdev, void *priv) 119{ 120 struct oxnas_dwmac *dwmac = priv; 121 int ret; 122 123 /* Reset HW here before changing the glue configuration */ 124 ret = device_reset(dwmac->dev); 125 if (ret) 126 return ret; 127 128 ret = clk_prepare_enable(dwmac->clk); 129 if (ret) 130 return ret; 131 132 ret = dwmac->data->setup(dwmac); 133 if (ret) 134 clk_disable_unprepare(dwmac->clk); 135 136 return ret; 137} 138 139static void oxnas_dwmac_exit(struct platform_device *pdev, void *priv) 140{ 141 struct oxnas_dwmac *dwmac = priv; 142 143 clk_disable_unprepare(dwmac->clk); 144} 145 146static int oxnas_dwmac_probe(struct platform_device *pdev) 147{ 148 struct plat_stmmacenet_data *plat_dat; 149 struct stmmac_resources stmmac_res; 150 struct oxnas_dwmac *dwmac; 151 int ret; 152 153 ret = stmmac_get_platform_resources(pdev, &stmmac_res); 154 if (ret) 155 return ret; 156 157 plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac); 158 if (IS_ERR(plat_dat)) 159 return PTR_ERR(plat_dat); 160 161 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 162 if (!dwmac) { 163 ret = -ENOMEM; 164 goto err_remove_config_dt; 165 } 166 167 dwmac->data = (const struct oxnas_dwmac_data *)of_device_get_match_data(&pdev->dev); 168 if (!dwmac->data) { 169 ret = -EINVAL; 170 goto err_remove_config_dt; 171 } 172 173 dwmac->dev = &pdev->dev; 174 plat_dat->bsp_priv = dwmac; 175 plat_dat->init = oxnas_dwmac_init; 176 plat_dat->exit = oxnas_dwmac_exit; 177 178 dwmac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 179 "oxsemi,sys-ctrl"); 180 if (IS_ERR(dwmac->regmap)) { 181 dev_err(&pdev->dev, "failed to have sysctrl regmap\n"); 182 ret = PTR_ERR(dwmac->regmap); 183 goto err_remove_config_dt; 184 } 185 186 dwmac->clk = devm_clk_get(&pdev->dev, "gmac"); 187 if (IS_ERR(dwmac->clk)) { 188 ret = PTR_ERR(dwmac->clk); 189 goto err_remove_config_dt; 190 } 191 192 ret = oxnas_dwmac_init(pdev, plat_dat->bsp_priv); 193 if (ret) 194 goto err_remove_config_dt; 195 196 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); 197 if (ret) 198 goto err_dwmac_exit; 199 200 201 return 0; 202 203err_dwmac_exit: 204 oxnas_dwmac_exit(pdev, plat_dat->bsp_priv); 205err_remove_config_dt: 206 stmmac_remove_config_dt(pdev, plat_dat); 207 208 return ret; 209} 210 211static const struct oxnas_dwmac_data ox810se_dwmac_data = { 212 .setup = oxnas_dwmac_setup_ox810se, 213}; 214 215static const struct oxnas_dwmac_data ox820_dwmac_data = { 216 .setup = oxnas_dwmac_setup_ox820, 217}; 218 219static const struct of_device_id oxnas_dwmac_match[] = { 220 { 221 .compatible = "oxsemi,ox810se-dwmac", 222 .data = &ox810se_dwmac_data, 223 }, 224 { 225 .compatible = "oxsemi,ox820-dwmac", 226 .data = &ox820_dwmac_data, 227 }, 228 { } 229}; 230MODULE_DEVICE_TABLE(of, oxnas_dwmac_match); 231 232static struct platform_driver oxnas_dwmac_driver = { 233 .probe = oxnas_dwmac_probe, 234 .remove = stmmac_pltfr_remove, 235 .driver = { 236 .name = "oxnas-dwmac", 237 .pm = &stmmac_pltfr_pm_ops, 238 .of_match_table = oxnas_dwmac_match, 239 }, 240}; 241module_platform_driver(oxnas_dwmac_driver); 242 243MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 244MODULE_DESCRIPTION("Oxford Semiconductor OXNAS DWMAC glue layer"); 245MODULE_LICENSE("GPL v2");