18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/ethernet/ibm/emac/zmii.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for PowerPC 4xx on-chip ethernet controller, ZMII 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 * Armin Kuster <akuster@mvista.com> 178c2ecf20Sopenharmony_ci * Copyright 2001 MontaVista Softare 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 "core.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* ZMIIx_FER */ 298c2ecf20Sopenharmony_ci#define ZMII_FER_MDI(idx) (0x80000000 >> ((idx) * 4)) 308c2ecf20Sopenharmony_ci#define ZMII_FER_MDI_ALL (ZMII_FER_MDI(0) | ZMII_FER_MDI(1) | \ 318c2ecf20Sopenharmony_ci ZMII_FER_MDI(2) | ZMII_FER_MDI(3)) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define ZMII_FER_SMII(idx) (0x40000000 >> ((idx) * 4)) 348c2ecf20Sopenharmony_ci#define ZMII_FER_RMII(idx) (0x20000000 >> ((idx) * 4)) 358c2ecf20Sopenharmony_ci#define ZMII_FER_MII(idx) (0x10000000 >> ((idx) * 4)) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* ZMIIx_SSR */ 388c2ecf20Sopenharmony_ci#define ZMII_SSR_SCI(idx) (0x40000000 >> ((idx) * 4)) 398c2ecf20Sopenharmony_ci#define ZMII_SSR_FSS(idx) (0x20000000 >> ((idx) * 4)) 408c2ecf20Sopenharmony_ci#define ZMII_SSR_SP(idx) (0x10000000 >> ((idx) * 4)) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* ZMII only supports MII, RMII and SMII 438c2ecf20Sopenharmony_ci * we also support autodetection for backward compatibility 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic inline int zmii_valid_mode(int mode) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci return mode == PHY_INTERFACE_MODE_MII || 488c2ecf20Sopenharmony_ci mode == PHY_INTERFACE_MODE_RMII || 498c2ecf20Sopenharmony_ci mode == PHY_INTERFACE_MODE_SMII || 508c2ecf20Sopenharmony_ci mode == PHY_INTERFACE_MODE_NA; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic inline const char *zmii_mode_name(int mode) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci switch (mode) { 568c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 578c2ecf20Sopenharmony_ci return "MII"; 588c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RMII: 598c2ecf20Sopenharmony_ci return "RMII"; 608c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_SMII: 618c2ecf20Sopenharmony_ci return "SMII"; 628c2ecf20Sopenharmony_ci default: 638c2ecf20Sopenharmony_ci BUG(); 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic inline u32 zmii_mode_mask(int mode, int input) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci switch (mode) { 708c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 718c2ecf20Sopenharmony_ci return ZMII_FER_MII(input); 728c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RMII: 738c2ecf20Sopenharmony_ci return ZMII_FER_RMII(input); 748c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_SMII: 758c2ecf20Sopenharmony_ci return ZMII_FER_SMII(input); 768c2ecf20Sopenharmony_ci default: 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ciint zmii_attach(struct platform_device *ofdev, int input, 828c2ecf20Sopenharmony_ci phy_interface_t *mode) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct zmii_instance *dev = platform_get_drvdata(ofdev); 858c2ecf20Sopenharmony_ci struct zmii_regs __iomem *p = dev->base; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci ZMII_DBG(dev, "init(%d, %d)" NL, input, *mode); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!zmii_valid_mode(*mode)) { 908c2ecf20Sopenharmony_ci /* Probably an EMAC connected to RGMII, 918c2ecf20Sopenharmony_ci * but it still may need ZMII for MDIO so 928c2ecf20Sopenharmony_ci * we don't fail here. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci dev->users++; 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Autodetect ZMII mode if not specified. 1018c2ecf20Sopenharmony_ci * This is only for backward compatibility with the old driver. 1028c2ecf20Sopenharmony_ci * Please, always specify PHY mode in your board port to avoid 1038c2ecf20Sopenharmony_ci * any surprises. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci if (dev->mode == PHY_INTERFACE_MODE_NA) { 1068c2ecf20Sopenharmony_ci if (*mode == PHY_INTERFACE_MODE_NA) { 1078c2ecf20Sopenharmony_ci u32 r = dev->fer_save; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ZMII_DBG(dev, "autodetecting mode, FER = 0x%08x" NL, r); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (r & (ZMII_FER_MII(0) | ZMII_FER_MII(1))) 1128c2ecf20Sopenharmony_ci dev->mode = PHY_INTERFACE_MODE_MII; 1138c2ecf20Sopenharmony_ci else if (r & (ZMII_FER_RMII(0) | ZMII_FER_RMII(1))) 1148c2ecf20Sopenharmony_ci dev->mode = PHY_INTERFACE_MODE_RMII; 1158c2ecf20Sopenharmony_ci else 1168c2ecf20Sopenharmony_ci dev->mode = PHY_INTERFACE_MODE_SMII; 1178c2ecf20Sopenharmony_ci } else { 1188c2ecf20Sopenharmony_ci dev->mode = *mode; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci printk(KERN_NOTICE "%pOF: bridge in %s mode\n", 1218c2ecf20Sopenharmony_ci ofdev->dev.of_node, 1228c2ecf20Sopenharmony_ci zmii_mode_name(dev->mode)); 1238c2ecf20Sopenharmony_ci } else { 1248c2ecf20Sopenharmony_ci /* All inputs must use the same mode */ 1258c2ecf20Sopenharmony_ci if (*mode != PHY_INTERFACE_MODE_NA && *mode != dev->mode) { 1268c2ecf20Sopenharmony_ci printk(KERN_ERR 1278c2ecf20Sopenharmony_ci "%pOF: invalid mode %d specified for input %d\n", 1288c2ecf20Sopenharmony_ci ofdev->dev.of_node, *mode, input); 1298c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1308c2ecf20Sopenharmony_ci return -EINVAL; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Report back correct PHY mode, 1358c2ecf20Sopenharmony_ci * it may be used during PHY initialization. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci *mode = dev->mode; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Enable this input */ 1408c2ecf20Sopenharmony_ci out_be32(&p->fer, in_be32(&p->fer) | zmii_mode_mask(dev->mode, input)); 1418c2ecf20Sopenharmony_ci ++dev->users; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_civoid zmii_get_mdio(struct platform_device *ofdev, int input) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct zmii_instance *dev = platform_get_drvdata(ofdev); 1518c2ecf20Sopenharmony_ci u32 fer; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ZMII_DBG2(dev, "get_mdio(%d)" NL, input); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci fer = in_be32(&dev->base->fer) & ~ZMII_FER_MDI_ALL; 1588c2ecf20Sopenharmony_ci out_be32(&dev->base->fer, fer | ZMII_FER_MDI(input)); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_civoid zmii_put_mdio(struct platform_device *ofdev, int input) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct zmii_instance *dev = platform_get_drvdata(ofdev); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci ZMII_DBG2(dev, "put_mdio(%d)" NL, input); 1668c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_civoid zmii_set_speed(struct platform_device *ofdev, int input, int speed) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct zmii_instance *dev = platform_get_drvdata(ofdev); 1738c2ecf20Sopenharmony_ci u32 ssr; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci ssr = in_be32(&dev->base->ssr); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci ZMII_DBG(dev, "speed(%d, %d)" NL, input, speed); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (speed == SPEED_100) 1828c2ecf20Sopenharmony_ci ssr |= ZMII_SSR_SP(input); 1838c2ecf20Sopenharmony_ci else 1848c2ecf20Sopenharmony_ci ssr &= ~ZMII_SSR_SP(input); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci out_be32(&dev->base->ssr, ssr); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_civoid zmii_detach(struct platform_device *ofdev, int input) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct zmii_instance *dev = platform_get_drvdata(ofdev); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci BUG_ON(!dev || dev->users == 0); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ZMII_DBG(dev, "detach(%d)" NL, input); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Disable this input */ 2028c2ecf20Sopenharmony_ci out_be32(&dev->base->fer, 2038c2ecf20Sopenharmony_ci in_be32(&dev->base->fer) & ~zmii_mode_mask(dev->mode, input)); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci --dev->users; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciint zmii_get_regs_len(struct platform_device *ofdev) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci return sizeof(struct emac_ethtool_regs_subhdr) + 2138c2ecf20Sopenharmony_ci sizeof(struct zmii_regs); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_civoid *zmii_dump_regs(struct platform_device *ofdev, void *buf) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct zmii_instance *dev = platform_get_drvdata(ofdev); 2198c2ecf20Sopenharmony_ci struct emac_ethtool_regs_subhdr *hdr = buf; 2208c2ecf20Sopenharmony_ci struct zmii_regs *regs = (struct zmii_regs *)(hdr + 1); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci hdr->version = 0; 2238c2ecf20Sopenharmony_ci hdr->index = 0; /* for now, are there chips with more than one 2248c2ecf20Sopenharmony_ci * zmii ? if yes, then we'll add a cell_index 2258c2ecf20Sopenharmony_ci * like we do for emac 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci memcpy_fromio(regs, dev->base, sizeof(struct zmii_regs)); 2288c2ecf20Sopenharmony_ci return regs + 1; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic int zmii_probe(struct platform_device *ofdev) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 2348c2ecf20Sopenharmony_ci struct zmii_instance *dev; 2358c2ecf20Sopenharmony_ci struct resource regs; 2368c2ecf20Sopenharmony_ci int rc; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci rc = -ENOMEM; 2398c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct zmii_instance), GFP_KERNEL); 2408c2ecf20Sopenharmony_ci if (dev == NULL) 2418c2ecf20Sopenharmony_ci goto err_gone; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci mutex_init(&dev->lock); 2448c2ecf20Sopenharmony_ci dev->ofdev = ofdev; 2458c2ecf20Sopenharmony_ci dev->mode = PHY_INTERFACE_MODE_NA; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci rc = -ENXIO; 2488c2ecf20Sopenharmony_ci if (of_address_to_resource(np, 0, ®s)) { 2498c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get registers address\n", np); 2508c2ecf20Sopenharmony_ci goto err_free; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci rc = -ENOMEM; 2548c2ecf20Sopenharmony_ci dev->base = (struct zmii_regs __iomem *)ioremap(regs.start, 2558c2ecf20Sopenharmony_ci sizeof(struct zmii_regs)); 2568c2ecf20Sopenharmony_ci if (dev->base == NULL) { 2578c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: Can't map device registers!\n", np); 2588c2ecf20Sopenharmony_ci goto err_free; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* We may need FER value for autodetection later */ 2628c2ecf20Sopenharmony_ci dev->fer_save = in_be32(&dev->base->fer); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Disable all inputs by default */ 2658c2ecf20Sopenharmony_ci out_be32(&dev->base->fer, 0); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci printk(KERN_INFO "ZMII %pOF initialized\n", ofdev->dev.of_node); 2688c2ecf20Sopenharmony_ci wmb(); 2698c2ecf20Sopenharmony_ci platform_set_drvdata(ofdev, dev); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci err_free: 2748c2ecf20Sopenharmony_ci kfree(dev); 2758c2ecf20Sopenharmony_ci err_gone: 2768c2ecf20Sopenharmony_ci return rc; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int zmii_remove(struct platform_device *ofdev) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct zmii_instance *dev = platform_get_drvdata(ofdev); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci WARN_ON(dev->users != 0); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci iounmap(dev->base); 2868c2ecf20Sopenharmony_ci kfree(dev); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return 0; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic const struct of_device_id zmii_match[] = 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci { 2948c2ecf20Sopenharmony_ci .compatible = "ibm,zmii", 2958c2ecf20Sopenharmony_ci }, 2968c2ecf20Sopenharmony_ci /* For backward compat with old DT */ 2978c2ecf20Sopenharmony_ci { 2988c2ecf20Sopenharmony_ci .type = "emac-zmii", 2998c2ecf20Sopenharmony_ci }, 3008c2ecf20Sopenharmony_ci {}, 3018c2ecf20Sopenharmony_ci}; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic struct platform_driver zmii_driver = { 3048c2ecf20Sopenharmony_ci .driver = { 3058c2ecf20Sopenharmony_ci .name = "emac-zmii", 3068c2ecf20Sopenharmony_ci .of_match_table = zmii_match, 3078c2ecf20Sopenharmony_ci }, 3088c2ecf20Sopenharmony_ci .probe = zmii_probe, 3098c2ecf20Sopenharmony_ci .remove = zmii_remove, 3108c2ecf20Sopenharmony_ci}; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ciint __init zmii_init(void) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci return platform_driver_register(&zmii_driver); 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_civoid zmii_exit(void) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci platform_driver_unregister(&zmii_driver); 3208c2ecf20Sopenharmony_ci} 321