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