xref: /openbmc/linux/drivers/net/ethernet/broadcom/bgmac-platform.c (revision 55e43d6abd078ed6d219902ce8cb4d68e3c993ba)
1f6a95a24SJon Mason /*
2f6a95a24SJon Mason  * Copyright (C) 2016 Broadcom
3f6a95a24SJon Mason  *
4f6a95a24SJon Mason  * This program is free software; you can redistribute it and/or
5f6a95a24SJon Mason  * modify it under the terms of the GNU General Public License as
6f6a95a24SJon Mason  * published by the Free Software Foundation version 2.
7f6a95a24SJon Mason  *
8f6a95a24SJon Mason  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9f6a95a24SJon Mason  * kind, whether express or implied; without even the implied warranty
10f6a95a24SJon Mason  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11f6a95a24SJon Mason  * GNU General Public License for more details.
12f6a95a24SJon Mason  */
13f6a95a24SJon Mason 
14f6a95a24SJon Mason #define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
15f6a95a24SJon Mason 
16f6a95a24SJon Mason #include <linux/bcma/bcma.h>
17dd5c5d03SJon Mason #include <linux/brcmphy.h>
18f6a95a24SJon Mason #include <linux/etherdevice.h>
19f6a95a24SJon Mason #include <linux/of_address.h>
201676aba5SJon Mason #include <linux/of_mdio.h>
21f6a95a24SJon Mason #include <linux/of_net.h>
22f6a95a24SJon Mason #include "bgmac.h"
23f6a95a24SJon Mason 
24f3537b34SJoey Zhong #define NICPM_PADRING_CFG		0x00000004
25dd5c5d03SJon Mason #define NICPM_IOMUX_CTRL		0x00000008
26dd5c5d03SJon Mason 
27f3537b34SJoey Zhong #define NICPM_PADRING_CFG_INIT_VAL	0x74000000
28f3537b34SJoey Zhong #define NICPM_IOMUX_CTRL_INIT_VAL_AX	0x21880000
29f3537b34SJoey Zhong 
30dd5c5d03SJon Mason #define NICPM_IOMUX_CTRL_INIT_VAL	0x3196e000
31dd5c5d03SJon Mason #define NICPM_IOMUX_CTRL_SPD_SHIFT	10
32dd5c5d03SJon Mason #define NICPM_IOMUX_CTRL_SPD_10M	0
33dd5c5d03SJon Mason #define NICPM_IOMUX_CTRL_SPD_100M	1
34dd5c5d03SJon Mason #define NICPM_IOMUX_CTRL_SPD_1000M	2
35dd5c5d03SJon Mason 
platform_bgmac_read(struct bgmac * bgmac,u16 offset)36f6a95a24SJon Mason static u32 platform_bgmac_read(struct bgmac *bgmac, u16 offset)
37f6a95a24SJon Mason {
38f6a95a24SJon Mason 	return readl(bgmac->plat.base + offset);
39f6a95a24SJon Mason }
40f6a95a24SJon Mason 
platform_bgmac_write(struct bgmac * bgmac,u16 offset,u32 value)41f6a95a24SJon Mason static void platform_bgmac_write(struct bgmac *bgmac, u16 offset, u32 value)
42f6a95a24SJon Mason {
43f6a95a24SJon Mason 	writel(value, bgmac->plat.base + offset);
44f6a95a24SJon Mason }
45f6a95a24SJon Mason 
platform_bgmac_idm_read(struct bgmac * bgmac,u16 offset)46f6a95a24SJon Mason static u32 platform_bgmac_idm_read(struct bgmac *bgmac, u16 offset)
47f6a95a24SJon Mason {
48f6a95a24SJon Mason 	return readl(bgmac->plat.idm_base + offset);
49f6a95a24SJon Mason }
50f6a95a24SJon Mason 
platform_bgmac_idm_write(struct bgmac * bgmac,u16 offset,u32 value)51f6a95a24SJon Mason static void platform_bgmac_idm_write(struct bgmac *bgmac, u16 offset, u32 value)
52f6a95a24SJon Mason {
5383a5c5afSAbhishek Shah 	writel(value, bgmac->plat.idm_base + offset);
54f6a95a24SJon Mason }
55f6a95a24SJon Mason 
platform_bgmac_clk_enabled(struct bgmac * bgmac)56f6a95a24SJon Mason static bool platform_bgmac_clk_enabled(struct bgmac *bgmac)
57f6a95a24SJon Mason {
58a163bdb0SAbhishek Shah 	if (!bgmac->plat.idm_base)
59a163bdb0SAbhishek Shah 		return true;
60a163bdb0SAbhishek Shah 
6116206524SJon Mason 	if ((bgmac_idm_read(bgmac, BCMA_IOCTL) & BGMAC_CLK_EN) != BGMAC_CLK_EN)
62f6a95a24SJon Mason 		return false;
63f6a95a24SJon Mason 	if (bgmac_idm_read(bgmac, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET)
64f6a95a24SJon Mason 		return false;
65f6a95a24SJon Mason 	return true;
66f6a95a24SJon Mason }
67f6a95a24SJon Mason 
platform_bgmac_clk_enable(struct bgmac * bgmac,u32 flags)68f6a95a24SJon Mason static void platform_bgmac_clk_enable(struct bgmac *bgmac, u32 flags)
69f6a95a24SJon Mason {
7016206524SJon Mason 	u32 val;
71f6a95a24SJon Mason 
72a163bdb0SAbhishek Shah 	if (!bgmac->plat.idm_base)
73a163bdb0SAbhishek Shah 		return;
74a163bdb0SAbhishek Shah 
7516206524SJon Mason 	/* The Reset Control register only contains a single bit to show if the
7616206524SJon Mason 	 * controller is currently in reset.  Do a sanity check here, just in
7716206524SJon Mason 	 * case the bootloader happened to leave the device in reset.
7816206524SJon Mason 	 */
7916206524SJon Mason 	val = bgmac_idm_read(bgmac, BCMA_RESET_CTL);
8016206524SJon Mason 	if (val) {
81f6a95a24SJon Mason 		bgmac_idm_write(bgmac, BCMA_RESET_CTL, 0);
82f6a95a24SJon Mason 		bgmac_idm_read(bgmac, BCMA_RESET_CTL);
83f6a95a24SJon Mason 		udelay(1);
8416206524SJon Mason 	}
85f6a95a24SJon Mason 
8616206524SJon Mason 	val = bgmac_idm_read(bgmac, BCMA_IOCTL);
8716206524SJon Mason 	/* Some bits of BCMA_IOCTL set by HW/ATF and should not change */
8816206524SJon Mason 	val |= flags & ~(BGMAC_AWCACHE | BGMAC_ARCACHE | BGMAC_AWUSER |
8916206524SJon Mason 			 BGMAC_ARUSER);
9016206524SJon Mason 	val |= BGMAC_CLK_EN;
9116206524SJon Mason 	bgmac_idm_write(bgmac, BCMA_IOCTL, val);
92f6a95a24SJon Mason 	bgmac_idm_read(bgmac, BCMA_IOCTL);
93f6a95a24SJon Mason 	udelay(1);
94f6a95a24SJon Mason }
95f6a95a24SJon Mason 
platform_bgmac_cco_ctl_maskset(struct bgmac * bgmac,u32 offset,u32 mask,u32 set)96f6a95a24SJon Mason static void platform_bgmac_cco_ctl_maskset(struct bgmac *bgmac, u32 offset,
97f6a95a24SJon Mason 					   u32 mask, u32 set)
98f6a95a24SJon Mason {
99f6a95a24SJon Mason 	/* This shouldn't be encountered */
100f6a95a24SJon Mason 	WARN_ON(1);
101f6a95a24SJon Mason }
102f6a95a24SJon Mason 
platform_bgmac_get_bus_clock(struct bgmac * bgmac)103f6a95a24SJon Mason static u32 platform_bgmac_get_bus_clock(struct bgmac *bgmac)
104f6a95a24SJon Mason {
105f6a95a24SJon Mason 	/* This shouldn't be encountered */
106f6a95a24SJon Mason 	WARN_ON(1);
107f6a95a24SJon Mason 
108f6a95a24SJon Mason 	return 0;
109f6a95a24SJon Mason }
110f6a95a24SJon Mason 
platform_bgmac_cmn_maskset32(struct bgmac * bgmac,u16 offset,u32 mask,u32 set)111f6a95a24SJon Mason static void platform_bgmac_cmn_maskset32(struct bgmac *bgmac, u16 offset,
112f6a95a24SJon Mason 					 u32 mask, u32 set)
113f6a95a24SJon Mason {
114f6a95a24SJon Mason 	/* This shouldn't be encountered */
115f6a95a24SJon Mason 	WARN_ON(1);
116f6a95a24SJon Mason }
117f6a95a24SJon Mason 
bgmac_nicpm_speed_set(struct net_device * net_dev)118dd5c5d03SJon Mason static void bgmac_nicpm_speed_set(struct net_device *net_dev)
119dd5c5d03SJon Mason {
120dd5c5d03SJon Mason 	struct bgmac *bgmac = netdev_priv(net_dev);
121dd5c5d03SJon Mason 	u32 val;
122dd5c5d03SJon Mason 
123dd5c5d03SJon Mason 	if (!bgmac->plat.nicpm_base)
124dd5c5d03SJon Mason 		return;
125dd5c5d03SJon Mason 
126f3537b34SJoey Zhong 	/* SET RGMII IO CONFIG */
127f3537b34SJoey Zhong 	writel(NICPM_PADRING_CFG_INIT_VAL,
128f3537b34SJoey Zhong 	       bgmac->plat.nicpm_base + NICPM_PADRING_CFG);
129f3537b34SJoey Zhong 
130dd5c5d03SJon Mason 	val = NICPM_IOMUX_CTRL_INIT_VAL;
131dd5c5d03SJon Mason 	switch (bgmac->net_dev->phydev->speed) {
132dd5c5d03SJon Mason 	default:
133dd5c5d03SJon Mason 		netdev_err(net_dev, "Unsupported speed. Defaulting to 1000Mb\n");
134df561f66SGustavo A. R. Silva 		fallthrough;
135dd5c5d03SJon Mason 	case SPEED_1000:
136dd5c5d03SJon Mason 		val |= NICPM_IOMUX_CTRL_SPD_1000M << NICPM_IOMUX_CTRL_SPD_SHIFT;
137dd5c5d03SJon Mason 		break;
138dd5c5d03SJon Mason 	case SPEED_100:
139dd5c5d03SJon Mason 		val |= NICPM_IOMUX_CTRL_SPD_100M << NICPM_IOMUX_CTRL_SPD_SHIFT;
140dd5c5d03SJon Mason 		break;
141dd5c5d03SJon Mason 	case SPEED_10:
142dd5c5d03SJon Mason 		val |= NICPM_IOMUX_CTRL_SPD_10M << NICPM_IOMUX_CTRL_SPD_SHIFT;
143dd5c5d03SJon Mason 		break;
144dd5c5d03SJon Mason 	}
145dd5c5d03SJon Mason 
146dd5c5d03SJon Mason 	writel(val, bgmac->plat.nicpm_base + NICPM_IOMUX_CTRL);
147dd5c5d03SJon Mason 
148dd5c5d03SJon Mason 	bgmac_adjust_link(bgmac->net_dev);
149dd5c5d03SJon Mason }
150dd5c5d03SJon Mason 
platform_phy_connect(struct bgmac * bgmac)1511676aba5SJon Mason static int platform_phy_connect(struct bgmac *bgmac)
1521676aba5SJon Mason {
1531676aba5SJon Mason 	struct phy_device *phy_dev;
1541676aba5SJon Mason 
155dd5c5d03SJon Mason 	if (bgmac->plat.nicpm_base)
156dd5c5d03SJon Mason 		phy_dev = of_phy_get_and_connect(bgmac->net_dev,
157dd5c5d03SJon Mason 						 bgmac->dev->of_node,
158dd5c5d03SJon Mason 						 bgmac_nicpm_speed_set);
159dd5c5d03SJon Mason 	else
160dd5c5d03SJon Mason 		phy_dev = of_phy_get_and_connect(bgmac->net_dev,
161dd5c5d03SJon Mason 						 bgmac->dev->of_node,
1621676aba5SJon Mason 						 bgmac_adjust_link);
1631676aba5SJon Mason 	if (!phy_dev) {
1641676aba5SJon Mason 		dev_err(bgmac->dev, "PHY connection failed\n");
1651676aba5SJon Mason 		return -ENODEV;
1661676aba5SJon Mason 	}
1671676aba5SJon Mason 
1681676aba5SJon Mason 	return 0;
1691676aba5SJon Mason }
1701676aba5SJon Mason 
bgmac_probe(struct platform_device * pdev)171f6a95a24SJon Mason static int bgmac_probe(struct platform_device *pdev)
172f6a95a24SJon Mason {
173f6a95a24SJon Mason 	struct device_node *np = pdev->dev.of_node;
174*4dfa9a92SJoe Hattori 	struct device_node *phy_node;
175f6a95a24SJon Mason 	struct bgmac *bgmac;
1766aba04eeSJonas Gorski 	struct resource *regs;
17783216e39SMichael Walle 	int ret;
178f6a95a24SJon Mason 
17934a5102cSRafał Miłecki 	bgmac = bgmac_alloc(&pdev->dev);
180f6a95a24SJon Mason 	if (!bgmac)
181f6a95a24SJon Mason 		return -ENOMEM;
182f6a95a24SJon Mason 
183f6a95a24SJon Mason 	platform_set_drvdata(pdev, bgmac);
184f6a95a24SJon Mason 
185f6a95a24SJon Mason 	/* Set the features of the 4707 family */
186f6a95a24SJon Mason 	bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST;
187f6a95a24SJon Mason 	bgmac->feature_flags |= BGMAC_FEAT_NO_RESET;
188f6a95a24SJon Mason 	bgmac->feature_flags |= BGMAC_FEAT_CMDCFG_SR_REV4;
189f6a95a24SJon Mason 	bgmac->feature_flags |= BGMAC_FEAT_TX_MASK_SETUP;
190f6a95a24SJon Mason 	bgmac->feature_flags |= BGMAC_FEAT_RX_MASK_SETUP;
191a163bdb0SAbhishek Shah 	bgmac->feature_flags |= BGMAC_FEAT_IDM_MASK;
192f6a95a24SJon Mason 
193f6a95a24SJon Mason 	bgmac->dev = &pdev->dev;
194f6a95a24SJon Mason 	bgmac->dma_dev = &pdev->dev;
195f6a95a24SJon Mason 
1969ca01b25SJakub Kicinski 	ret = of_get_ethdev_address(np, bgmac->net_dev);
197763716a5SMatthew Hagan 	if (ret == -EPROBE_DEFER)
198763716a5SMatthew Hagan 		return ret;
199763716a5SMatthew Hagan 
20083216e39SMichael Walle 	if (ret)
20183216e39SMichael Walle 		dev_warn(&pdev->dev,
20283216e39SMichael Walle 			 "MAC address not present in device tree\n");
203f6a95a24SJon Mason 
204f6a95a24SJon Mason 	bgmac->irq = platform_get_irq(pdev, 0);
205d1a55841SStephen Boyd 	if (bgmac->irq < 0)
206f6a95a24SJon Mason 		return bgmac->irq;
207f6a95a24SJon Mason 
208d7a5502bSDejin Zheng 	bgmac->plat.base =
209d7a5502bSDejin Zheng 		devm_platform_ioremap_resource_byname(pdev, "amac_base");
210ce3a380dSWei Yongjun 	if (IS_ERR(bgmac->plat.base))
211f6a95a24SJon Mason 		return PTR_ERR(bgmac->plat.base);
212f6a95a24SJon Mason 
2136aba04eeSJonas Gorski 	/* The idm_base resource is optional for some platforms */
2146aba04eeSJonas Gorski 	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "idm_base");
2156aba04eeSJonas Gorski 	if (regs) {
2166aba04eeSJonas Gorski 		bgmac->plat.idm_base = devm_ioremap_resource(&pdev->dev, regs);
217ce3a380dSWei Yongjun 		if (IS_ERR(bgmac->plat.idm_base))
218f6a95a24SJon Mason 			return PTR_ERR(bgmac->plat.idm_base);
219a163bdb0SAbhishek Shah 		bgmac->feature_flags &= ~BGMAC_FEAT_IDM_MASK;
2206aba04eeSJonas Gorski 	}
221f6a95a24SJon Mason 
2226aba04eeSJonas Gorski 	/* The nicpm_base resource is optional for some platforms */
2236aba04eeSJonas Gorski 	regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nicpm_base");
2246aba04eeSJonas Gorski 	if (regs) {
2256aba04eeSJonas Gorski 		bgmac->plat.nicpm_base = devm_ioremap_resource(&pdev->dev,
2266aba04eeSJonas Gorski 							       regs);
227dd5c5d03SJon Mason 		if (IS_ERR(bgmac->plat.nicpm_base))
228dd5c5d03SJon Mason 			return PTR_ERR(bgmac->plat.nicpm_base);
2296aba04eeSJonas Gorski 	}
230dd5c5d03SJon Mason 
231f6a95a24SJon Mason 	bgmac->read = platform_bgmac_read;
232f6a95a24SJon Mason 	bgmac->write = platform_bgmac_write;
233f6a95a24SJon Mason 	bgmac->idm_read = platform_bgmac_idm_read;
234f6a95a24SJon Mason 	bgmac->idm_write = platform_bgmac_idm_write;
235f6a95a24SJon Mason 	bgmac->clk_enabled = platform_bgmac_clk_enabled;
236f6a95a24SJon Mason 	bgmac->clk_enable = platform_bgmac_clk_enable;
237f6a95a24SJon Mason 	bgmac->cco_ctl_maskset = platform_bgmac_cco_ctl_maskset;
238f6a95a24SJon Mason 	bgmac->get_bus_clock = platform_bgmac_get_bus_clock;
239f6a95a24SJon Mason 	bgmac->cmn_maskset32 = platform_bgmac_cmn_maskset32;
240*4dfa9a92SJoe Hattori 	phy_node = of_parse_phandle(np, "phy-handle", 0);
241*4dfa9a92SJoe Hattori 	if (phy_node) {
242*4dfa9a92SJoe Hattori 		of_node_put(phy_node);
2431676aba5SJon Mason 		bgmac->phy_connect = platform_phy_connect;
2441676aba5SJon Mason 	} else {
2451676aba5SJon Mason 		bgmac->phy_connect = bgmac_phy_connect_direct;
2461676aba5SJon Mason 		bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500;
2471676aba5SJon Mason 	}
248f6a95a24SJon Mason 
249f6a95a24SJon Mason 	return bgmac_enet_probe(bgmac);
250f6a95a24SJon Mason }
251f6a95a24SJon Mason 
bgmac_remove(struct platform_device * pdev)252f6a95a24SJon Mason static int bgmac_remove(struct platform_device *pdev)
253f6a95a24SJon Mason {
254f6a95a24SJon Mason 	struct bgmac *bgmac = platform_get_drvdata(pdev);
255f6a95a24SJon Mason 
256f6a95a24SJon Mason 	bgmac_enet_remove(bgmac);
257f6a95a24SJon Mason 
258f6a95a24SJon Mason 	return 0;
259f6a95a24SJon Mason }
260f6a95a24SJon Mason 
261f3537b34SJoey Zhong #ifdef CONFIG_PM
bgmac_suspend(struct device * dev)262f3537b34SJoey Zhong static int bgmac_suspend(struct device *dev)
263f3537b34SJoey Zhong {
264f3537b34SJoey Zhong 	struct bgmac *bgmac = dev_get_drvdata(dev);
265f3537b34SJoey Zhong 
266f3537b34SJoey Zhong 	return bgmac_enet_suspend(bgmac);
267f3537b34SJoey Zhong }
268f3537b34SJoey Zhong 
bgmac_resume(struct device * dev)269f3537b34SJoey Zhong static int bgmac_resume(struct device *dev)
270f3537b34SJoey Zhong {
271f3537b34SJoey Zhong 	struct bgmac *bgmac = dev_get_drvdata(dev);
272f3537b34SJoey Zhong 
273f3537b34SJoey Zhong 	return bgmac_enet_resume(bgmac);
274f3537b34SJoey Zhong }
275f3537b34SJoey Zhong 
276f3537b34SJoey Zhong static const struct dev_pm_ops bgmac_pm_ops = {
277f3537b34SJoey Zhong 	.suspend = bgmac_suspend,
278f3537b34SJoey Zhong 	.resume = bgmac_resume
279f3537b34SJoey Zhong };
280f3537b34SJoey Zhong 
281f3537b34SJoey Zhong #define BGMAC_PM_OPS (&bgmac_pm_ops)
282f3537b34SJoey Zhong #else
283f3537b34SJoey Zhong #define BGMAC_PM_OPS NULL
284f3537b34SJoey Zhong #endif /* CONFIG_PM */
285f3537b34SJoey Zhong 
286f6a95a24SJon Mason static const struct of_device_id bgmac_of_enet_match[] = {
287f6a95a24SJon Mason 	{.compatible = "brcm,amac",},
288f6a95a24SJon Mason 	{.compatible = "brcm,nsp-amac",},
289dd5c5d03SJon Mason 	{.compatible = "brcm,ns2-amac",},
290f6a95a24SJon Mason 	{},
291f6a95a24SJon Mason };
292f6a95a24SJon Mason 
293f6a95a24SJon Mason MODULE_DEVICE_TABLE(of, bgmac_of_enet_match);
294f6a95a24SJon Mason 
295f6a95a24SJon Mason static struct platform_driver bgmac_enet_driver = {
296f6a95a24SJon Mason 	.driver = {
297f6a95a24SJon Mason 		.name  = "bgmac-enet",
298f6a95a24SJon Mason 		.of_match_table = bgmac_of_enet_match,
299f3537b34SJoey Zhong 		.pm = BGMAC_PM_OPS
300f6a95a24SJon Mason 	},
301f6a95a24SJon Mason 	.probe = bgmac_probe,
302f6a95a24SJon Mason 	.remove = bgmac_remove,
303f6a95a24SJon Mason };
304f6a95a24SJon Mason 
305f6a95a24SJon Mason module_platform_driver(bgmac_enet_driver);
306f6a95a24SJon Mason MODULE_LICENSE("GPL");
307