blob: e2e0f977875d7026b030564c3828af022271721f [file] [log] [blame]
Nobuhiro Iwamatsub38dd982021-02-16 00:24:36 +09001// SPDX-License-Identifier: GPL-2.0
2/* Toshiba Visconti Ethernet Support
3 *
4 * (C) Copyright 2020 TOSHIBA CORPORATION
5 * (C) Copyright 2020 Toshiba Electronic Devices & Storage Corporation
6 */
7
8#include <linux/module.h>
9#include <linux/of_device.h>
10#include <linux/of_net.h>
11#include <linux/stmmac.h>
12
13#include "stmmac_platform.h"
14#include "dwmac4.h"
15
16#define REG_ETHER_CONTROL 0x52D4
17#define ETHER_ETH_CONTROL_RESET BIT(17)
18
19#define REG_ETHER_CLOCK_SEL 0x52D0
20#define ETHER_CLK_SEL_TX_CLK_EN BIT(0)
21#define ETHER_CLK_SEL_RX_CLK_EN BIT(1)
22#define ETHER_CLK_SEL_RMII_CLK_EN BIT(2)
23#define ETHER_CLK_SEL_RMII_CLK_RST BIT(3)
24#define ETHER_CLK_SEL_DIV_SEL_2 BIT(4)
25#define ETHER_CLK_SEL_DIV_SEL_20 BIT(0)
26#define ETHER_CLK_SEL_FREQ_SEL_125M (BIT(9) | BIT(8))
27#define ETHER_CLK_SEL_FREQ_SEL_50M BIT(9)
28#define ETHER_CLK_SEL_FREQ_SEL_25M BIT(8)
Nobuhiro Iwamatsu391e5972021-12-23 16:36:33 +090029#define ETHER_CLK_SEL_FREQ_SEL_2P5M 0
Nobuhiro Iwamatsub38dd982021-02-16 00:24:36 +090030#define ETHER_CLK_SEL_TX_CLK_EXT_SEL_IN BIT(0)
31#define ETHER_CLK_SEL_TX_CLK_EXT_SEL_TXC BIT(10)
32#define ETHER_CLK_SEL_TX_CLK_EXT_SEL_DIV BIT(11)
33#define ETHER_CLK_SEL_RX_CLK_EXT_SEL_IN BIT(0)
34#define ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC BIT(12)
35#define ETHER_CLK_SEL_RX_CLK_EXT_SEL_DIV BIT(13)
36#define ETHER_CLK_SEL_TX_CLK_O_TX_I BIT(0)
37#define ETHER_CLK_SEL_TX_CLK_O_RMII_I BIT(14)
38#define ETHER_CLK_SEL_TX_O_E_N_IN BIT(15)
39#define ETHER_CLK_SEL_RMII_CLK_SEL_IN BIT(0)
40#define ETHER_CLK_SEL_RMII_CLK_SEL_RX_C BIT(16)
41
42#define ETHER_CLK_SEL_RX_TX_CLK_EN (ETHER_CLK_SEL_RX_CLK_EN | ETHER_CLK_SEL_TX_CLK_EN)
43
44#define ETHER_CONFIG_INTF_MII 0
45#define ETHER_CONFIG_INTF_RGMII BIT(0)
46#define ETHER_CONFIG_INTF_RMII BIT(2)
47
48struct visconti_eth {
49 void __iomem *reg;
50 u32 phy_intf_sel;
51 struct clk *phy_ref_clk;
52 spinlock_t lock; /* lock to protect register update */
53};
54
55static void visconti_eth_fix_mac_speed(void *priv, unsigned int speed)
56{
57 struct visconti_eth *dwmac = priv;
58 unsigned int val, clk_sel_val;
59 unsigned long flags;
60
61 spin_lock_irqsave(&dwmac->lock, flags);
62
63 /* adjust link */
64 val = readl(dwmac->reg + MAC_CTRL_REG);
65 val &= ~(GMAC_CONFIG_PS | GMAC_CONFIG_FES);
66
67 switch (speed) {
68 case SPEED_1000:
69 if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RGMII)
70 clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_125M;
71 break;
72 case SPEED_100:
73 if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RGMII)
74 clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_25M;
75 if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RMII)
76 clk_sel_val = ETHER_CLK_SEL_DIV_SEL_2;
77 val |= GMAC_CONFIG_PS | GMAC_CONFIG_FES;
78 break;
79 case SPEED_10:
80 if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RGMII)
81 clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_2P5M;
82 if (dwmac->phy_intf_sel == ETHER_CONFIG_INTF_RMII)
83 clk_sel_val = ETHER_CLK_SEL_DIV_SEL_20;
84 val |= GMAC_CONFIG_PS;
85 break;
86 default:
87 /* No bit control */
88 break;
89 }
90
91 writel(val, dwmac->reg + MAC_CTRL_REG);
92
93 /* Stop internal clock */
94 val = readl(dwmac->reg + REG_ETHER_CLOCK_SEL);
95 val &= ~(ETHER_CLK_SEL_RMII_CLK_EN | ETHER_CLK_SEL_RX_TX_CLK_EN);
96 val |= ETHER_CLK_SEL_TX_O_E_N_IN;
97 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL);
98
99 switch (dwmac->phy_intf_sel) {
100 case ETHER_CONFIG_INTF_RGMII:
101 val = clk_sel_val | ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC;
102 break;
103 case ETHER_CONFIG_INTF_RMII:
104 val = clk_sel_val | ETHER_CLK_SEL_RX_CLK_EXT_SEL_DIV |
105 ETHER_CLK_SEL_TX_CLK_EXT_SEL_TXC | ETHER_CLK_SEL_TX_O_E_N_IN |
106 ETHER_CLK_SEL_RMII_CLK_SEL_RX_C;
107 break;
108 case ETHER_CONFIG_INTF_MII:
109 default:
110 val = clk_sel_val | ETHER_CLK_SEL_RX_CLK_EXT_SEL_RXC |
111 ETHER_CLK_SEL_TX_CLK_EXT_SEL_DIV | ETHER_CLK_SEL_TX_O_E_N_IN |
112 ETHER_CLK_SEL_RMII_CLK_EN;
113 break;
114 }
115
116 /* Start clock */
117 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL);
118 val |= ETHER_CLK_SEL_RX_TX_CLK_EN;
119 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL);
120
121 val &= ~ETHER_CLK_SEL_TX_O_E_N_IN;
122 writel(val, dwmac->reg + REG_ETHER_CLOCK_SEL);
123
124 spin_unlock_irqrestore(&dwmac->lock, flags);
125}
126
127static int visconti_eth_init_hw(struct platform_device *pdev, struct plat_stmmacenet_data *plat_dat)
128{
129 struct visconti_eth *dwmac = plat_dat->bsp_priv;
130 unsigned int reg_val, clk_sel_val;
131
132 switch (plat_dat->phy_interface) {
133 case PHY_INTERFACE_MODE_RGMII:
134 case PHY_INTERFACE_MODE_RGMII_ID:
135 case PHY_INTERFACE_MODE_RGMII_RXID:
136 case PHY_INTERFACE_MODE_RGMII_TXID:
137 dwmac->phy_intf_sel = ETHER_CONFIG_INTF_RGMII;
138 break;
139 case PHY_INTERFACE_MODE_MII:
140 dwmac->phy_intf_sel = ETHER_CONFIG_INTF_MII;
141 break;
142 case PHY_INTERFACE_MODE_RMII:
143 dwmac->phy_intf_sel = ETHER_CONFIG_INTF_RMII;
144 break;
145 default:
146 dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n", plat_dat->phy_interface);
147 return -EOPNOTSUPP;
148 }
149
150 reg_val = dwmac->phy_intf_sel;
151 writel(reg_val, dwmac->reg + REG_ETHER_CONTROL);
152
153 /* Enable TX/RX clock */
154 clk_sel_val = ETHER_CLK_SEL_FREQ_SEL_125M;
155 writel(clk_sel_val, dwmac->reg + REG_ETHER_CLOCK_SEL);
156
157 writel((clk_sel_val | ETHER_CLK_SEL_RMII_CLK_EN | ETHER_CLK_SEL_RX_TX_CLK_EN),
158 dwmac->reg + REG_ETHER_CLOCK_SEL);
159
160 /* release internal-reset */
161 reg_val |= ETHER_ETH_CONTROL_RESET;
162 writel(reg_val, dwmac->reg + REG_ETHER_CONTROL);
163
164 return 0;
165}
166
167static int visconti_eth_clock_probe(struct platform_device *pdev,
168 struct plat_stmmacenet_data *plat_dat)
169{
170 struct visconti_eth *dwmac = plat_dat->bsp_priv;
171 int err;
172
173 dwmac->phy_ref_clk = devm_clk_get(&pdev->dev, "phy_ref_clk");
Cai Huoqingb20b54f2021-09-16 15:37:36 +0800174 if (IS_ERR(dwmac->phy_ref_clk))
175 return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->phy_ref_clk),
176 "phy_ref_clk clock not found.\n");
Nobuhiro Iwamatsub38dd982021-02-16 00:24:36 +0900177
178 err = clk_prepare_enable(dwmac->phy_ref_clk);
179 if (err < 0) {
180 dev_err(&pdev->dev, "failed to enable phy_ref clock: %d\n", err);
181 return err;
182 }
183
184 return 0;
185}
186
187static int visconti_eth_clock_remove(struct platform_device *pdev)
188{
189 struct visconti_eth *dwmac = get_stmmac_bsp_priv(&pdev->dev);
190 struct net_device *ndev = platform_get_drvdata(pdev);
191 struct stmmac_priv *priv = netdev_priv(ndev);
192
193 clk_disable_unprepare(dwmac->phy_ref_clk);
194 clk_disable_unprepare(priv->plat->stmmac_clk);
195
196 return 0;
197}
198
199static int visconti_eth_dwmac_probe(struct platform_device *pdev)
200{
201 struct plat_stmmacenet_data *plat_dat;
202 struct stmmac_resources stmmac_res;
203 struct visconti_eth *dwmac;
204 int ret;
205
206 ret = stmmac_get_platform_resources(pdev, &stmmac_res);
207 if (ret)
208 return ret;
209
Michael Walle83216e32021-04-12 19:47:17 +0200210 plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac);
Nobuhiro Iwamatsub38dd982021-02-16 00:24:36 +0900211 if (IS_ERR(plat_dat))
212 return PTR_ERR(plat_dat);
213
214 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
215 if (!dwmac) {
216 ret = -ENOMEM;
217 goto remove_config;
218 }
219
Wei Yongjun17d7fd42021-02-23 10:48:03 +0000220 spin_lock_init(&dwmac->lock);
Nobuhiro Iwamatsub38dd982021-02-16 00:24:36 +0900221 dwmac->reg = stmmac_res.addr;
222 plat_dat->bsp_priv = dwmac;
223 plat_dat->fix_mac_speed = visconti_eth_fix_mac_speed;
224
225 ret = visconti_eth_clock_probe(pdev, plat_dat);
226 if (ret)
227 goto remove_config;
228
229 visconti_eth_init_hw(pdev, plat_dat);
230
231 plat_dat->dma_cfg->aal = 1;
232
233 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
234 if (ret)
235 goto remove;
236
237 return ret;
238
239remove:
240 visconti_eth_clock_remove(pdev);
241remove_config:
242 stmmac_remove_config_dt(pdev, plat_dat);
243
244 return ret;
245}
246
247static int visconti_eth_dwmac_remove(struct platform_device *pdev)
248{
249 struct net_device *ndev = platform_get_drvdata(pdev);
250 struct stmmac_priv *priv = netdev_priv(ndev);
251 int err;
252
253 err = stmmac_pltfr_remove(pdev);
254 if (err < 0)
255 dev_err(&pdev->dev, "failed to remove platform: %d\n", err);
256
257 err = visconti_eth_clock_remove(pdev);
258 if (err < 0)
259 dev_err(&pdev->dev, "failed to remove clock: %d\n", err);
260
261 stmmac_remove_config_dt(pdev, priv->plat);
262
263 return err;
264}
265
266static const struct of_device_id visconti_eth_dwmac_match[] = {
267 { .compatible = "toshiba,visconti-dwmac" },
268 { }
269};
270MODULE_DEVICE_TABLE(of, visconti_eth_dwmac_match);
271
272static struct platform_driver visconti_eth_dwmac_driver = {
273 .probe = visconti_eth_dwmac_probe,
274 .remove = visconti_eth_dwmac_remove,
275 .driver = {
276 .name = "visconti-eth-dwmac",
277 .of_match_table = visconti_eth_dwmac_match,
278 },
279};
280module_platform_driver(visconti_eth_dwmac_driver);
281
282MODULE_AUTHOR("Toshiba");
283MODULE_DESCRIPTION("Toshiba Visconti Ethernet DWMAC glue driver");
284MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp");
285MODULE_LICENSE("GPL v2");