18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/ethernet/ibm/emac/tah.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Driver for PowerPC 4xx on-chip ethernet controller, TAH 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 2004 MontaVista Software, Inc. 138c2ecf20Sopenharmony_ci * Matt Porter <mporter@kernel.crashing.org> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Copyright (c) 2005 Eugene Surovegin <ebs@ebshome.net> 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <asm/io.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "emac.h" 218c2ecf20Sopenharmony_ci#include "core.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciint tah_attach(struct platform_device *ofdev, int channel) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct tah_instance *dev = platform_get_drvdata(ofdev); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 288c2ecf20Sopenharmony_ci /* Reset has been done at probe() time... nothing else to do for now */ 298c2ecf20Sopenharmony_ci ++dev->users; 308c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return 0; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_civoid tah_detach(struct platform_device *ofdev, int channel) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct tah_instance *dev = platform_get_drvdata(ofdev); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 408c2ecf20Sopenharmony_ci --dev->users; 418c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_civoid tah_reset(struct platform_device *ofdev) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct tah_instance *dev = platform_get_drvdata(ofdev); 478c2ecf20Sopenharmony_ci struct tah_regs __iomem *p = dev->base; 488c2ecf20Sopenharmony_ci int n; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci /* Reset TAH */ 518c2ecf20Sopenharmony_ci out_be32(&p->mr, TAH_MR_SR); 528c2ecf20Sopenharmony_ci n = 100; 538c2ecf20Sopenharmony_ci while ((in_be32(&p->mr) & TAH_MR_SR) && n) 548c2ecf20Sopenharmony_ci --n; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (unlikely(!n)) 578c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: reset timeout\n", ofdev->dev.of_node); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* 10KB TAH TX FIFO accommodates the max MTU of 9000 */ 608c2ecf20Sopenharmony_ci out_be32(&p->mr, 618c2ecf20Sopenharmony_ci TAH_MR_CVR | TAH_MR_ST_768 | TAH_MR_TFS_10KB | TAH_MR_DTFP | 628c2ecf20Sopenharmony_ci TAH_MR_DIG); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ciint tah_get_regs_len(struct platform_device *ofdev) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci return sizeof(struct emac_ethtool_regs_subhdr) + 688c2ecf20Sopenharmony_ci sizeof(struct tah_regs); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_civoid *tah_dump_regs(struct platform_device *ofdev, void *buf) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct tah_instance *dev = platform_get_drvdata(ofdev); 748c2ecf20Sopenharmony_ci struct emac_ethtool_regs_subhdr *hdr = buf; 758c2ecf20Sopenharmony_ci struct tah_regs *regs = (struct tah_regs *)(hdr + 1); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci hdr->version = 0; 788c2ecf20Sopenharmony_ci hdr->index = 0; /* for now, are there chips with more than one 798c2ecf20Sopenharmony_ci * zmii ? if yes, then we'll add a cell_index 808c2ecf20Sopenharmony_ci * like we do for emac 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci memcpy_fromio(regs, dev->base, sizeof(struct tah_regs)); 838c2ecf20Sopenharmony_ci return regs + 1; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int tah_probe(struct platform_device *ofdev) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct device_node *np = ofdev->dev.of_node; 898c2ecf20Sopenharmony_ci struct tah_instance *dev; 908c2ecf20Sopenharmony_ci struct resource regs; 918c2ecf20Sopenharmony_ci int rc; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci rc = -ENOMEM; 948c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(struct tah_instance), GFP_KERNEL); 958c2ecf20Sopenharmony_ci if (dev == NULL) 968c2ecf20Sopenharmony_ci goto err_gone; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci mutex_init(&dev->lock); 998c2ecf20Sopenharmony_ci dev->ofdev = ofdev; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci rc = -ENXIO; 1028c2ecf20Sopenharmony_ci if (of_address_to_resource(np, 0, ®s)) { 1038c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get registers address\n", np); 1048c2ecf20Sopenharmony_ci goto err_free; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci rc = -ENOMEM; 1088c2ecf20Sopenharmony_ci dev->base = (struct tah_regs __iomem *)ioremap(regs.start, 1098c2ecf20Sopenharmony_ci sizeof(struct tah_regs)); 1108c2ecf20Sopenharmony_ci if (dev->base == NULL) { 1118c2ecf20Sopenharmony_ci printk(KERN_ERR "%pOF: Can't map device registers!\n", np); 1128c2ecf20Sopenharmony_ci goto err_free; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci platform_set_drvdata(ofdev, dev); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ 1188c2ecf20Sopenharmony_ci tah_reset(ofdev); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci printk(KERN_INFO "TAH %pOF initialized\n", ofdev->dev.of_node); 1218c2ecf20Sopenharmony_ci wmb(); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci err_free: 1268c2ecf20Sopenharmony_ci kfree(dev); 1278c2ecf20Sopenharmony_ci err_gone: 1288c2ecf20Sopenharmony_ci return rc; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int tah_remove(struct platform_device *ofdev) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct tah_instance *dev = platform_get_drvdata(ofdev); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci WARN_ON(dev->users != 0); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci iounmap(dev->base); 1388c2ecf20Sopenharmony_ci kfree(dev); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct of_device_id tah_match[] = 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci { 1468c2ecf20Sopenharmony_ci .compatible = "ibm,tah", 1478c2ecf20Sopenharmony_ci }, 1488c2ecf20Sopenharmony_ci /* For backward compat with old DT */ 1498c2ecf20Sopenharmony_ci { 1508c2ecf20Sopenharmony_ci .type = "tah", 1518c2ecf20Sopenharmony_ci }, 1528c2ecf20Sopenharmony_ci {}, 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic struct platform_driver tah_driver = { 1568c2ecf20Sopenharmony_ci .driver = { 1578c2ecf20Sopenharmony_ci .name = "emac-tah", 1588c2ecf20Sopenharmony_ci .of_match_table = tah_match, 1598c2ecf20Sopenharmony_ci }, 1608c2ecf20Sopenharmony_ci .probe = tah_probe, 1618c2ecf20Sopenharmony_ci .remove = tah_remove, 1628c2ecf20Sopenharmony_ci}; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciint __init tah_init(void) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci return platform_driver_register(&tah_driver); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_civoid tah_exit(void) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci platform_driver_unregister(&tah_driver); 1728c2ecf20Sopenharmony_ci} 173