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
52 struct oxnas_dwmac;
53
54 struct oxnas_dwmac_data {
55 int (*setup)(struct oxnas_dwmac *dwmac);
56 };
57
58 struct oxnas_dwmac {
59 struct device *dev;
60 struct clk *clk;
61 struct regmap *regmap;
62 const struct oxnas_dwmac_data *data;
63 };
64
oxnas_dwmac_setup_ox810se(struct oxnas_dwmac * dwmac)65 static 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
oxnas_dwmac_setup_ox820(struct oxnas_dwmac * dwmac)84 static 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
oxnas_dwmac_init(struct platform_device * pdev,void * priv)118 static 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
oxnas_dwmac_exit(struct platform_device * pdev,void * priv)139 static 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
oxnas_dwmac_probe(struct platform_device * pdev)146 static 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
203 err_dwmac_exit:
204 oxnas_dwmac_exit(pdev, plat_dat->bsp_priv);
205 err_remove_config_dt:
206 stmmac_remove_config_dt(pdev, plat_dat);
207
208 return ret;
209 }
210
211 static const struct oxnas_dwmac_data ox810se_dwmac_data = {
212 .setup = oxnas_dwmac_setup_ox810se,
213 };
214
215 static const struct oxnas_dwmac_data ox820_dwmac_data = {
216 .setup = oxnas_dwmac_setup_ox820,
217 };
218
219 static 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 };
230 MODULE_DEVICE_TABLE(of, oxnas_dwmac_match);
231
232 static 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 };
241 module_platform_driver(oxnas_dwmac_driver);
242
243 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
244 MODULE_DESCRIPTION("Oxford Semiconductor OXNAS DWMAC glue layer");
245 MODULE_LICENSE("GPL v2");
246