18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (C) 2016 Broadcom 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 58c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 68c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 98c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 108c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 118c2ecf20Sopenharmony_ci * GNU General Public License for more details. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/bcma/bcma.h> 178c2ecf20Sopenharmony_ci#include <linux/brcmphy.h> 188c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 198c2ecf20Sopenharmony_ci#include <linux/of_address.h> 208c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 218c2ecf20Sopenharmony_ci#include <linux/of_net.h> 228c2ecf20Sopenharmony_ci#include "bgmac.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define NICPM_PADRING_CFG 0x00000004 258c2ecf20Sopenharmony_ci#define NICPM_IOMUX_CTRL 0x00000008 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define NICPM_PADRING_CFG_INIT_VAL 0x74000000 288c2ecf20Sopenharmony_ci#define NICPM_IOMUX_CTRL_INIT_VAL_AX 0x21880000 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define NICPM_IOMUX_CTRL_INIT_VAL 0x3196e000 318c2ecf20Sopenharmony_ci#define NICPM_IOMUX_CTRL_SPD_SHIFT 10 328c2ecf20Sopenharmony_ci#define NICPM_IOMUX_CTRL_SPD_10M 0 338c2ecf20Sopenharmony_ci#define NICPM_IOMUX_CTRL_SPD_100M 1 348c2ecf20Sopenharmony_ci#define NICPM_IOMUX_CTRL_SPD_1000M 2 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic u32 platform_bgmac_read(struct bgmac *bgmac, u16 offset) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return readl(bgmac->plat.base + offset); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void platform_bgmac_write(struct bgmac *bgmac, u16 offset, u32 value) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci writel(value, bgmac->plat.base + offset); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic u32 platform_bgmac_idm_read(struct bgmac *bgmac, u16 offset) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci return readl(bgmac->plat.idm_base + offset); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic void platform_bgmac_idm_write(struct bgmac *bgmac, u16 offset, u32 value) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci writel(value, bgmac->plat.idm_base + offset); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic bool platform_bgmac_clk_enabled(struct bgmac *bgmac) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci if (!bgmac->plat.idm_base) 598c2ecf20Sopenharmony_ci return true; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if ((bgmac_idm_read(bgmac, BCMA_IOCTL) & BGMAC_CLK_EN) != BGMAC_CLK_EN) 628c2ecf20Sopenharmony_ci return false; 638c2ecf20Sopenharmony_ci if (bgmac_idm_read(bgmac, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET) 648c2ecf20Sopenharmony_ci return false; 658c2ecf20Sopenharmony_ci return true; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void platform_bgmac_clk_enable(struct bgmac *bgmac, u32 flags) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci u32 val; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!bgmac->plat.idm_base) 738c2ecf20Sopenharmony_ci return; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* The Reset Control register only contains a single bit to show if the 768c2ecf20Sopenharmony_ci * controller is currently in reset. Do a sanity check here, just in 778c2ecf20Sopenharmony_ci * case the bootloader happened to leave the device in reset. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci val = bgmac_idm_read(bgmac, BCMA_RESET_CTL); 808c2ecf20Sopenharmony_ci if (val) { 818c2ecf20Sopenharmony_ci bgmac_idm_write(bgmac, BCMA_RESET_CTL, 0); 828c2ecf20Sopenharmony_ci bgmac_idm_read(bgmac, BCMA_RESET_CTL); 838c2ecf20Sopenharmony_ci udelay(1); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci val = bgmac_idm_read(bgmac, BCMA_IOCTL); 878c2ecf20Sopenharmony_ci /* Some bits of BCMA_IOCTL set by HW/ATF and should not change */ 888c2ecf20Sopenharmony_ci val |= flags & ~(BGMAC_AWCACHE | BGMAC_ARCACHE | BGMAC_AWUSER | 898c2ecf20Sopenharmony_ci BGMAC_ARUSER); 908c2ecf20Sopenharmony_ci val |= BGMAC_CLK_EN; 918c2ecf20Sopenharmony_ci bgmac_idm_write(bgmac, BCMA_IOCTL, val); 928c2ecf20Sopenharmony_ci bgmac_idm_read(bgmac, BCMA_IOCTL); 938c2ecf20Sopenharmony_ci udelay(1); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void platform_bgmac_cco_ctl_maskset(struct bgmac *bgmac, u32 offset, 978c2ecf20Sopenharmony_ci u32 mask, u32 set) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci /* This shouldn't be encountered */ 1008c2ecf20Sopenharmony_ci WARN_ON(1); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic u32 platform_bgmac_get_bus_clock(struct bgmac *bgmac) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci /* This shouldn't be encountered */ 1068c2ecf20Sopenharmony_ci WARN_ON(1); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void platform_bgmac_cmn_maskset32(struct bgmac *bgmac, u16 offset, 1128c2ecf20Sopenharmony_ci u32 mask, u32 set) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci /* This shouldn't be encountered */ 1158c2ecf20Sopenharmony_ci WARN_ON(1); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void bgmac_nicpm_speed_set(struct net_device *net_dev) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct bgmac *bgmac = netdev_priv(net_dev); 1218c2ecf20Sopenharmony_ci u32 val; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!bgmac->plat.nicpm_base) 1248c2ecf20Sopenharmony_ci return; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* SET RGMII IO CONFIG */ 1278c2ecf20Sopenharmony_ci writel(NICPM_PADRING_CFG_INIT_VAL, 1288c2ecf20Sopenharmony_ci bgmac->plat.nicpm_base + NICPM_PADRING_CFG); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci val = NICPM_IOMUX_CTRL_INIT_VAL; 1318c2ecf20Sopenharmony_ci switch (bgmac->net_dev->phydev->speed) { 1328c2ecf20Sopenharmony_ci default: 1338c2ecf20Sopenharmony_ci netdev_err(net_dev, "Unsupported speed. Defaulting to 1000Mb\n"); 1348c2ecf20Sopenharmony_ci fallthrough; 1358c2ecf20Sopenharmony_ci case SPEED_1000: 1368c2ecf20Sopenharmony_ci val |= NICPM_IOMUX_CTRL_SPD_1000M << NICPM_IOMUX_CTRL_SPD_SHIFT; 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci case SPEED_100: 1398c2ecf20Sopenharmony_ci val |= NICPM_IOMUX_CTRL_SPD_100M << NICPM_IOMUX_CTRL_SPD_SHIFT; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci case SPEED_10: 1428c2ecf20Sopenharmony_ci val |= NICPM_IOMUX_CTRL_SPD_10M << NICPM_IOMUX_CTRL_SPD_SHIFT; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci writel(val, bgmac->plat.nicpm_base + NICPM_IOMUX_CTRL); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci bgmac_adjust_link(bgmac->net_dev); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int platform_phy_connect(struct bgmac *bgmac) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct phy_device *phy_dev; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (bgmac->plat.nicpm_base) 1568c2ecf20Sopenharmony_ci phy_dev = of_phy_get_and_connect(bgmac->net_dev, 1578c2ecf20Sopenharmony_ci bgmac->dev->of_node, 1588c2ecf20Sopenharmony_ci bgmac_nicpm_speed_set); 1598c2ecf20Sopenharmony_ci else 1608c2ecf20Sopenharmony_ci phy_dev = of_phy_get_and_connect(bgmac->net_dev, 1618c2ecf20Sopenharmony_ci bgmac->dev->of_node, 1628c2ecf20Sopenharmony_ci bgmac_adjust_link); 1638c2ecf20Sopenharmony_ci if (!phy_dev) { 1648c2ecf20Sopenharmony_ci dev_err(bgmac->dev, "PHY connection failed\n"); 1658c2ecf20Sopenharmony_ci return -ENODEV; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci return 0; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic int bgmac_probe(struct platform_device *pdev) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 1748c2ecf20Sopenharmony_ci struct bgmac *bgmac; 1758c2ecf20Sopenharmony_ci struct resource *regs; 1768c2ecf20Sopenharmony_ci const u8 *mac_addr; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci bgmac = bgmac_alloc(&pdev->dev); 1798c2ecf20Sopenharmony_ci if (!bgmac) 1808c2ecf20Sopenharmony_ci return -ENOMEM; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, bgmac); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Set the features of the 4707 family */ 1858c2ecf20Sopenharmony_ci bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST; 1868c2ecf20Sopenharmony_ci bgmac->feature_flags |= BGMAC_FEAT_NO_RESET; 1878c2ecf20Sopenharmony_ci bgmac->feature_flags |= BGMAC_FEAT_CMDCFG_SR_REV4; 1888c2ecf20Sopenharmony_ci bgmac->feature_flags |= BGMAC_FEAT_TX_MASK_SETUP; 1898c2ecf20Sopenharmony_ci bgmac->feature_flags |= BGMAC_FEAT_RX_MASK_SETUP; 1908c2ecf20Sopenharmony_ci bgmac->feature_flags |= BGMAC_FEAT_IDM_MASK; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci bgmac->dev = &pdev->dev; 1938c2ecf20Sopenharmony_ci bgmac->dma_dev = &pdev->dev; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci mac_addr = of_get_mac_address(np); 1968c2ecf20Sopenharmony_ci if (!IS_ERR(mac_addr)) 1978c2ecf20Sopenharmony_ci ether_addr_copy(bgmac->net_dev->dev_addr, mac_addr); 1988c2ecf20Sopenharmony_ci else 1998c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "MAC address not present in device tree\n"); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci bgmac->irq = platform_get_irq(pdev, 0); 2028c2ecf20Sopenharmony_ci if (bgmac->irq < 0) 2038c2ecf20Sopenharmony_ci return bgmac->irq; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci bgmac->plat.base = 2068c2ecf20Sopenharmony_ci devm_platform_ioremap_resource_byname(pdev, "amac_base"); 2078c2ecf20Sopenharmony_ci if (IS_ERR(bgmac->plat.base)) 2088c2ecf20Sopenharmony_ci return PTR_ERR(bgmac->plat.base); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "idm_base"); 2118c2ecf20Sopenharmony_ci if (regs) { 2128c2ecf20Sopenharmony_ci bgmac->plat.idm_base = devm_ioremap_resource(&pdev->dev, regs); 2138c2ecf20Sopenharmony_ci if (IS_ERR(bgmac->plat.idm_base)) 2148c2ecf20Sopenharmony_ci return PTR_ERR(bgmac->plat.idm_base); 2158c2ecf20Sopenharmony_ci bgmac->feature_flags &= ~BGMAC_FEAT_IDM_MASK; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nicpm_base"); 2198c2ecf20Sopenharmony_ci if (regs) { 2208c2ecf20Sopenharmony_ci bgmac->plat.nicpm_base = devm_ioremap_resource(&pdev->dev, 2218c2ecf20Sopenharmony_ci regs); 2228c2ecf20Sopenharmony_ci if (IS_ERR(bgmac->plat.nicpm_base)) 2238c2ecf20Sopenharmony_ci return PTR_ERR(bgmac->plat.nicpm_base); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci bgmac->read = platform_bgmac_read; 2278c2ecf20Sopenharmony_ci bgmac->write = platform_bgmac_write; 2288c2ecf20Sopenharmony_ci bgmac->idm_read = platform_bgmac_idm_read; 2298c2ecf20Sopenharmony_ci bgmac->idm_write = platform_bgmac_idm_write; 2308c2ecf20Sopenharmony_ci bgmac->clk_enabled = platform_bgmac_clk_enabled; 2318c2ecf20Sopenharmony_ci bgmac->clk_enable = platform_bgmac_clk_enable; 2328c2ecf20Sopenharmony_ci bgmac->cco_ctl_maskset = platform_bgmac_cco_ctl_maskset; 2338c2ecf20Sopenharmony_ci bgmac->get_bus_clock = platform_bgmac_get_bus_clock; 2348c2ecf20Sopenharmony_ci bgmac->cmn_maskset32 = platform_bgmac_cmn_maskset32; 2358c2ecf20Sopenharmony_ci if (of_parse_phandle(np, "phy-handle", 0)) { 2368c2ecf20Sopenharmony_ci bgmac->phy_connect = platform_phy_connect; 2378c2ecf20Sopenharmony_ci } else { 2388c2ecf20Sopenharmony_ci bgmac->phy_connect = bgmac_phy_connect_direct; 2398c2ecf20Sopenharmony_ci bgmac->feature_flags |= BGMAC_FEAT_FORCE_SPEED_2500; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return bgmac_enet_probe(bgmac); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int bgmac_remove(struct platform_device *pdev) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct bgmac *bgmac = platform_get_drvdata(pdev); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci bgmac_enet_remove(bgmac); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2558c2ecf20Sopenharmony_cistatic int bgmac_suspend(struct device *dev) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct bgmac *bgmac = dev_get_drvdata(dev); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return bgmac_enet_suspend(bgmac); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int bgmac_resume(struct device *dev) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct bgmac *bgmac = dev_get_drvdata(dev); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return bgmac_enet_resume(bgmac); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic const struct dev_pm_ops bgmac_pm_ops = { 2708c2ecf20Sopenharmony_ci .suspend = bgmac_suspend, 2718c2ecf20Sopenharmony_ci .resume = bgmac_resume 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci#define BGMAC_PM_OPS (&bgmac_pm_ops) 2758c2ecf20Sopenharmony_ci#else 2768c2ecf20Sopenharmony_ci#define BGMAC_PM_OPS NULL 2778c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const struct of_device_id bgmac_of_enet_match[] = { 2808c2ecf20Sopenharmony_ci {.compatible = "brcm,amac",}, 2818c2ecf20Sopenharmony_ci {.compatible = "brcm,nsp-amac",}, 2828c2ecf20Sopenharmony_ci {.compatible = "brcm,ns2-amac",}, 2838c2ecf20Sopenharmony_ci {}, 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bgmac_of_enet_match); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic struct platform_driver bgmac_enet_driver = { 2898c2ecf20Sopenharmony_ci .driver = { 2908c2ecf20Sopenharmony_ci .name = "bgmac-enet", 2918c2ecf20Sopenharmony_ci .of_match_table = bgmac_of_enet_match, 2928c2ecf20Sopenharmony_ci .pm = BGMAC_PM_OPS 2938c2ecf20Sopenharmony_ci }, 2948c2ecf20Sopenharmony_ci .probe = bgmac_probe, 2958c2ecf20Sopenharmony_ci .remove = bgmac_remove, 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cimodule_platform_driver(bgmac_enet_driver); 2998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 300