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, &regs)) {
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