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_address.h>
21#include <linux/of_net.h>
22#include <linux/phy.h>
23#include <linux/regmap.h>
24#include <linux/reset.h>
25#include <linux/stmmac.h>
26
27#include "stmmac.h"
28#include "stmmac_platform.h"
29
30#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
31#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
32#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2
33#define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2
34#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK 0x00000003
35
36#define EMAC_SPLITTER_CTRL_REG			0x0
37#define EMAC_SPLITTER_CTRL_SPEED_MASK		0x3
38#define EMAC_SPLITTER_CTRL_SPEED_10		0x2
39#define EMAC_SPLITTER_CTRL_SPEED_100		0x3
40#define EMAC_SPLITTER_CTRL_SPEED_1000		0x0
41
42struct socfpga_dwmac {
43	int	interface;
44	u32	reg_offset;
45	u32	reg_shift;
46	struct	device *dev;
47	struct regmap *sys_mgr_base_addr;
48	struct reset_control *stmmac_rst;
49	void __iomem *splitter_base;
50};
51
52static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed)
53{
54	struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv;
55	void __iomem *splitter_base = dwmac->splitter_base;
56	u32 val;
57
58	if (!splitter_base)
59		return;
60
61	val = readl(splitter_base + EMAC_SPLITTER_CTRL_REG);
62	val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK;
63
64	switch (speed) {
65	case 1000:
66		val |= EMAC_SPLITTER_CTRL_SPEED_1000;
67		break;
68	case 100:
69		val |= EMAC_SPLITTER_CTRL_SPEED_100;
70		break;
71	case 10:
72		val |= EMAC_SPLITTER_CTRL_SPEED_10;
73		break;
74	default:
75		return;
76	}
77
78	writel(val, splitter_base + EMAC_SPLITTER_CTRL_REG);
79}
80
81static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev)
82{
83	struct device_node *np = dev->of_node;
84	struct regmap *sys_mgr_base_addr;
85	u32 reg_offset, reg_shift;
86	int ret;
87	struct device_node *np_splitter;
88	struct resource res_splitter;
89
90	dwmac->stmmac_rst = devm_reset_control_get(dev,
91						  STMMAC_RESOURCE_NAME);
92	if (IS_ERR(dwmac->stmmac_rst)) {
93		dev_info(dev, "Could not get reset control!\n");
94		if (PTR_ERR(dwmac->stmmac_rst) == -EPROBE_DEFER)
95			return -EPROBE_DEFER;
96		dwmac->stmmac_rst = NULL;
97	}
98
99	dwmac->interface = of_get_phy_mode(np);
100
101	sys_mgr_base_addr = syscon_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
102	if (IS_ERR(sys_mgr_base_addr)) {
103		dev_info(dev, "No sysmgr-syscon node found\n");
104		return PTR_ERR(sys_mgr_base_addr);
105	}
106
107	ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, &reg_offset);
108	if (ret) {
109		dev_info(dev, "Could not read reg_offset from sysmgr-syscon!\n");
110		return -EINVAL;
111	}
112
113	ret = of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, &reg_shift);
114	if (ret) {
115		dev_info(dev, "Could not read reg_shift from sysmgr-syscon!\n");
116		return -EINVAL;
117	}
118
119	np_splitter = of_parse_phandle(np, "altr,emac-splitter", 0);
120	if (np_splitter) {
121		if (of_address_to_resource(np_splitter, 0, &res_splitter)) {
122			dev_info(dev, "Missing emac splitter address\n");
123			return -EINVAL;
124		}
125
126		dwmac->splitter_base = devm_ioremap_resource(dev, &res_splitter);
127		if (IS_ERR(dwmac->splitter_base)) {
128			dev_info(dev, "Failed to mapping emac splitter\n");
129			return PTR_ERR(dwmac->splitter_base);
130		}
131	}
132
133	dwmac->reg_offset = reg_offset;
134	dwmac->reg_shift = reg_shift;
135	dwmac->sys_mgr_base_addr = sys_mgr_base_addr;
136	dwmac->dev = dev;
137
138	return 0;
139}
140
141static int socfpga_dwmac_setup(struct socfpga_dwmac *dwmac)
142{
143	struct regmap *sys_mgr_base_addr = dwmac->sys_mgr_base_addr;
144	int phymode = dwmac->interface;
145	u32 reg_offset = dwmac->reg_offset;
146	u32 reg_shift = dwmac->reg_shift;
147	u32 ctrl, val;
148
149	switch (phymode) {
150	case PHY_INTERFACE_MODE_RGMII:
151	case PHY_INTERFACE_MODE_RGMII_ID:
152		val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII;
153		break;
154	case PHY_INTERFACE_MODE_MII:
155	case PHY_INTERFACE_MODE_GMII:
156		val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
157		break;
158	default:
159		dev_err(dwmac->dev, "bad phy mode %d\n", phymode);
160		return -EINVAL;
161	}
162
163	/* Overwrite val to GMII if splitter core is enabled. The phymode here
164	 * is the actual phy mode on phy hardware, but phy interface from
165	 * EMAC core is GMII.
166	 */
167	if (dwmac->splitter_base)
168		val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII;
169
170	regmap_read(sys_mgr_base_addr, reg_offset, &ctrl);
171	ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
172	ctrl |= val << reg_shift;
173
174	regmap_write(sys_mgr_base_addr, reg_offset, ctrl);
175	return 0;
176}
177
178static void *socfpga_dwmac_probe(struct platform_device *pdev)
179{
180	struct device		*dev = &pdev->dev;
181	int			ret;
182	struct socfpga_dwmac	*dwmac;
183
184	dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
185	if (!dwmac)
186		return ERR_PTR(-ENOMEM);
187
188	ret = socfpga_dwmac_parse_data(dwmac, dev);
189	if (ret) {
190		dev_err(dev, "Unable to parse OF data\n");
191		return ERR_PTR(ret);
192	}
193
194	ret = socfpga_dwmac_setup(dwmac);
195	if (ret) {
196		dev_err(dev, "couldn't setup SoC glue (%d)\n", ret);
197		return ERR_PTR(ret);
198	}
199
200	return dwmac;
201}
202
203static void socfpga_dwmac_exit(struct platform_device *pdev, void *priv)
204{
205	struct socfpga_dwmac	*dwmac = priv;
206
207	/* On socfpga platform exit, assert and hold reset to the
208	 * enet controller - the default state after a hard reset.
209	 */
210	if (dwmac->stmmac_rst)
211		reset_control_assert(dwmac->stmmac_rst);
212}
213
214static int socfpga_dwmac_init(struct platform_device *pdev, void *priv)
215{
216	struct socfpga_dwmac	*dwmac = priv;
217	struct net_device *ndev = platform_get_drvdata(pdev);
218	struct stmmac_priv *stpriv = NULL;
219	int ret = 0;
220
221	if (ndev)
222		stpriv = netdev_priv(ndev);
223
224	/* Assert reset to the enet controller before changing the phy mode */
225	if (dwmac->stmmac_rst)
226		reset_control_assert(dwmac->stmmac_rst);
227
228	/* Setup the phy mode in the system manager registers according to
229	 * devicetree configuration
230	 */
231	ret = socfpga_dwmac_setup(dwmac);
232
233	/* Deassert reset for the phy configuration to be sampled by
234	 * the enet controller, and operation to start in requested mode
235	 */
236	if (dwmac->stmmac_rst)
237		reset_control_deassert(dwmac->stmmac_rst);
238
239	/* Before the enet controller is suspended, the phy is suspended.
240	 * This causes the phy clock to be gated. The enet controller is
241	 * resumed before the phy, so the clock is still gated "off" when
242	 * the enet controller is resumed. This code makes sure the phy
243	 * is "resumed" before reinitializing the enet controller since
244	 * the enet controller depends on an active phy clock to complete
245	 * a DMA reset. A DMA reset will "time out" if executed
246	 * with no phy clock input on the Synopsys enet controller.
247	 * Verified through Synopsys Case #8000711656.
248	 *
249	 * Note that the phy clock is also gated when the phy is isolated.
250	 * Phy "suspend" and "isolate" controls are located in phy basic
251	 * control register 0, and can be modified by the phy driver
252	 * framework.
253	 */
254	if (stpriv && stpriv->phydev)
255		phy_resume(stpriv->phydev);
256
257	return ret;
258}
259
260const struct stmmac_of_data socfpga_gmac_data = {
261	.setup = socfpga_dwmac_probe,
262	.init = socfpga_dwmac_init,
263	.exit = socfpga_dwmac_exit,
264	.fix_mac_speed = socfpga_dwmac_fix_mac_speed,
265};
266