18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/ethernet/ibm/emac/rgmii.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for PowerPC 4xx on-chip ethernet controller, RGMII bridge support. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. 88c2ecf20Sopenharmony_ci * <benh@kernel.crashing.org> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Based on the arch/ppc version of the driver: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (c) 2004, 2005 Zultys Technologies. 138c2ecf20Sopenharmony_ci * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Based on original work by 168c2ecf20Sopenharmony_ci * Matt Porter <mporter@kernel.crashing.org> 178c2ecf20Sopenharmony_ci * Copyright 2004 MontaVista Software, Inc. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 228c2ecf20Sopenharmony_ci#include <linux/of_address.h> 238c2ecf20Sopenharmony_ci#include <asm/io.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "emac.h" 268c2ecf20Sopenharmony_ci#include "debug.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci// XXX FIXME: Axon seems to support a subset of the RGMII, we 298c2ecf20Sopenharmony_ci// thus need to take that into account and possibly change some 308c2ecf20Sopenharmony_ci// of the bit settings below that don't seem to quite match the 318c2ecf20Sopenharmony_ci// AXON spec 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* RGMIIx_FER */ 348c2ecf20Sopenharmony_ci#define RGMII_FER_MASK(idx) (0x7 << ((idx) * 4)) 358c2ecf20Sopenharmony_ci#define RGMII_FER_RTBI(idx) (0x4 << ((idx) * 4)) 368c2ecf20Sopenharmony_ci#define RGMII_FER_RGMII(idx) (0x5 << ((idx) * 4)) 378c2ecf20Sopenharmony_ci#define RGMII_FER_TBI(idx) (0x6 << ((idx) * 4)) 388c2ecf20Sopenharmony_ci#define RGMII_FER_GMII(idx) (0x7 << ((idx) * 4)) 398c2ecf20Sopenharmony_ci#define RGMII_FER_MII(idx) RGMII_FER_GMII(idx) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* RGMIIx_SSR */ 428c2ecf20Sopenharmony_ci#define RGMII_SSR_MASK(idx) (0x7 << ((idx) * 8)) 438c2ecf20Sopenharmony_ci#define RGMII_SSR_10(idx) (0x1 << ((idx) * 8)) 448c2ecf20Sopenharmony_ci#define RGMII_SSR_100(idx) (0x2 << ((idx) * 8)) 458c2ecf20Sopenharmony_ci#define RGMII_SSR_1000(idx) (0x4 << ((idx) * 8)) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* RGMII bridge supports only GMII/TBI and RGMII/RTBI PHYs */ 488c2ecf20Sopenharmony_cistatic inline int rgmii_valid_mode(int phy_mode) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci return phy_interface_mode_is_rgmii(phy_mode) || 518c2ecf20Sopenharmony_ci phy_mode == PHY_INTERFACE_MODE_GMII || 528c2ecf20Sopenharmony_ci phy_mode == PHY_INTERFACE_MODE_MII || 538c2ecf20Sopenharmony_ci phy_mode == PHY_INTERFACE_MODE_TBI || 548c2ecf20Sopenharmony_ci phy_mode == PHY_INTERFACE_MODE_RTBI; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline u32 rgmii_mode_mask(int mode, int input) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci switch (mode) { 608c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 618c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 628c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 638c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 648c2ecf20Sopenharmony_ci return RGMII_FER_RGMII(input); 658c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_TBI: 668c2ecf20Sopenharmony_ci return RGMII_FER_TBI(input); 678c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_GMII: 688c2ecf20Sopenharmony_ci return RGMII_FER_GMII(input); 698c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 708c2ecf20Sopenharmony_ci return RGMII_FER_MII(input); 718c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RTBI: 728c2ecf20Sopenharmony_ci return RGMII_FER_RTBI(input); 738c2ecf20Sopenharmony_ci default: 748c2ecf20Sopenharmony_ci BUG(); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ciint rgmii_attach(struct platform_device *ofdev, int input, int mode) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci struct rgmii_instance *dev = platform_get_drvdata(ofdev); 818c2ecf20Sopenharmony_ci struct rgmii_regs __iomem *p = dev->base; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci RGMII_DBG(dev, "attach(%d)" NL, input); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* Check if we need to attach to a RGMII */ 868c2ecf20Sopenharmony_ci if (input < 0 || !rgmii_valid_mode(mode)) { 878c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: unsupported settings !\n", 888c2ecf20Sopenharmony_ci ofdev->dev.of_node); 898c2ecf20Sopenharmony_ci return -ENODEV; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Enable this input */ 958c2ecf20Sopenharmony_ci out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input)); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%pOF: input %d in %s mode\n", 988c2ecf20Sopenharmony_ci ofdev->dev.of_node, input, phy_modes(mode)); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ++dev->users; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return 0; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_civoid rgmii_set_speed(struct platform_device *ofdev, int input, int speed) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct rgmii_instance *dev = platform_get_drvdata(ofdev); 1108c2ecf20Sopenharmony_ci struct rgmii_regs __iomem *p = dev->base; 1118c2ecf20Sopenharmony_ci u32 ssr; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ssr = in_be32(&p->ssr) & ~RGMII_SSR_MASK(input); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci RGMII_DBG(dev, "speed(%d, %d)" NL, input, speed); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (speed == SPEED_1000) 1208c2ecf20Sopenharmony_ci ssr |= RGMII_SSR_1000(input); 1218c2ecf20Sopenharmony_ci else if (speed == SPEED_100) 1228c2ecf20Sopenharmony_ci ssr |= RGMII_SSR_100(input); 1238c2ecf20Sopenharmony_ci else if (speed == SPEED_10) 1248c2ecf20Sopenharmony_ci ssr |= RGMII_SSR_10(input); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci out_be32(&p->ssr, ssr); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_civoid rgmii_get_mdio(struct platform_device *ofdev, int input) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct rgmii_instance *dev = platform_get_drvdata(ofdev); 1348c2ecf20Sopenharmony_ci struct rgmii_regs __iomem *p = dev->base; 1358c2ecf20Sopenharmony_ci u32 fer; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci RGMII_DBG2(dev, "get_mdio(%d)" NL, input); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO)) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci fer = in_be32(&p->fer); 1458c2ecf20Sopenharmony_ci fer |= 0x00080000u >> input; 1468c2ecf20Sopenharmony_ci out_be32(&p->fer, fer); 1478c2ecf20Sopenharmony_ci (void)in_be32(&p->fer); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci DBG2(dev, " fer = 0x%08x\n", fer); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_civoid rgmii_put_mdio(struct platform_device *ofdev, int input) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct rgmii_instance *dev = platform_get_drvdata(ofdev); 1558c2ecf20Sopenharmony_ci struct rgmii_regs __iomem *p = dev->base; 1568c2ecf20Sopenharmony_ci u32 fer; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci RGMII_DBG2(dev, "put_mdio(%d)" NL, input); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!(dev->flags & EMAC_RGMII_FLAG_HAS_MDIO)) 1618c2ecf20Sopenharmony_ci return; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci fer = in_be32(&p->fer); 1648c2ecf20Sopenharmony_ci fer &= ~(0x00080000u >> input); 1658c2ecf20Sopenharmony_ci out_be32(&p->fer, fer); 1668c2ecf20Sopenharmony_ci (void)in_be32(&p->fer); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci DBG2(dev, " fer = 0x%08x\n", fer); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_civoid rgmii_detach(struct platform_device *ofdev, int input) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct rgmii_instance *dev = platform_get_drvdata(ofdev); 1768c2ecf20Sopenharmony_ci struct rgmii_regs __iomem *p; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci BUG_ON(!dev || dev->users == 0); 1798c2ecf20Sopenharmony_ci p = dev->base; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci RGMII_DBG(dev, "detach(%d)" NL, input); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Disable this input */ 1868c2ecf20Sopenharmony_ci out_be32(&p->fer, in_be32(&p->fer) & ~RGMII_FER_MASK(input)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci --dev->users; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciint rgmii_get_regs_len(struct platform_device *ofdev) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci return sizeof(struct emac_ethtool_regs_subhdr) + 1968c2ecf20Sopenharmony_ci sizeof(struct rgmii_regs); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_civoid *rgmii_dump_regs(struct platform_device *ofdev, void *buf) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct rgmii_instance *dev = platform_get_drvdata(ofdev); 2028c2ecf20Sopenharmony_ci struct emac_ethtool_regs_subhdr *hdr = buf; 2038c2ecf20Sopenharmony_ci struct rgmii_regs *regs = (struct rgmii_regs *)(hdr + 1); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci hdr->version = 0; 2068c2ecf20Sopenharmony_ci hdr->index = 0; /* for now, are there chips with more than one 2078c2ecf20Sopenharmony_ci * rgmii ? if yes, then we'll add a cell_index 2088c2ecf20Sopenharmony_ci * like we do for emac 2098c2ecf20Sopenharmony_ci */ 2108c2ecf20Sopenharmony_ci memcpy_fromio(regs, dev->base, sizeof(struct rgmii_regs)); 2118c2ecf20Sopenharmony_ci return regs + 1; 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int rgmii_probe(struct platform_device *ofdev) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 2188c2ecf20Sopenharmony_ci struct rgmii_instance *dev; 2198c2ecf20Sopenharmony_ci struct resource regs; 2208c2ecf20Sopenharmony_ci int rc; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci rc = -ENOMEM; 2238c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct rgmii_instance), GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (dev == NULL) 2258c2ecf20Sopenharmony_ci goto err_gone; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci mutex_init(&dev->lock); 2288c2ecf20Sopenharmony_ci dev->ofdev = ofdev; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci rc = -ENXIO; 2318c2ecf20Sopenharmony_ci if (of_address_to_resource(np, 0, ®s)) { 2328c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get registers address\n", np); 2338c2ecf20Sopenharmony_ci goto err_free; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci rc = -ENOMEM; 2378c2ecf20Sopenharmony_ci dev->base = (struct rgmii_regs __iomem *)ioremap(regs.start, 2388c2ecf20Sopenharmony_ci sizeof(struct rgmii_regs)); 2398c2ecf20Sopenharmony_ci if (dev->base == NULL) { 2408c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: Can't map device registers!\n", np); 2418c2ecf20Sopenharmony_ci goto err_free; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* Check for RGMII flags */ 2458c2ecf20Sopenharmony_ci if (of_get_property(ofdev->dev.of_node, "has-mdio", NULL)) 2468c2ecf20Sopenharmony_ci dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* CAB lacks the right properties, fix this up */ 2498c2ecf20Sopenharmony_ci if (of_device_is_compatible(ofdev->dev.of_node, "ibm,rgmii-axon")) 2508c2ecf20Sopenharmony_ci dev->flags |= EMAC_RGMII_FLAG_HAS_MDIO; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci DBG2(dev, " Boot FER = 0x%08x, SSR = 0x%08x\n", 2538c2ecf20Sopenharmony_ci in_be32(&dev->base->fer), in_be32(&dev->base->ssr)); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* Disable all inputs by default */ 2568c2ecf20Sopenharmony_ci out_be32(&dev->base->fer, 0); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci printk(KERN_INFO 2598c2ecf20Sopenharmony_ci "RGMII %pOF initialized with%s MDIO support\n", 2608c2ecf20Sopenharmony_ci ofdev->dev.of_node, 2618c2ecf20Sopenharmony_ci (dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out"); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci wmb(); 2648c2ecf20Sopenharmony_ci platform_set_drvdata(ofdev, dev); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci err_free: 2698c2ecf20Sopenharmony_ci kfree(dev); 2708c2ecf20Sopenharmony_ci err_gone: 2718c2ecf20Sopenharmony_ci return rc; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int rgmii_remove(struct platform_device *ofdev) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct rgmii_instance *dev = platform_get_drvdata(ofdev); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci WARN_ON(dev->users != 0); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci iounmap(dev->base); 2818c2ecf20Sopenharmony_ci kfree(dev); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic const struct of_device_id rgmii_match[] = 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci { 2898c2ecf20Sopenharmony_ci .compatible = "ibm,rgmii", 2908c2ecf20Sopenharmony_ci }, 2918c2ecf20Sopenharmony_ci { 2928c2ecf20Sopenharmony_ci .type = "emac-rgmii", 2938c2ecf20Sopenharmony_ci }, 2948c2ecf20Sopenharmony_ci {}, 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic struct platform_driver rgmii_driver = { 2988c2ecf20Sopenharmony_ci .driver = { 2998c2ecf20Sopenharmony_ci .name = "emac-rgmii", 3008c2ecf20Sopenharmony_ci .of_match_table = rgmii_match, 3018c2ecf20Sopenharmony_ci }, 3028c2ecf20Sopenharmony_ci .probe = rgmii_probe, 3038c2ecf20Sopenharmony_ci .remove = rgmii_remove, 3048c2ecf20Sopenharmony_ci}; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciint __init rgmii_init(void) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci return platform_driver_register(&rgmii_driver); 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_civoid rgmii_exit(void) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci platform_driver_unregister(&rgmii_driver); 3148c2ecf20Sopenharmony_ci} 315