162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/net/ethernet/ibm/emac/tah.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Driver for PowerPC 4xx on-chip ethernet controller, TAH support.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
862306a36Sopenharmony_ci *                <benh@kernel.crashing.org>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Based on the arch/ppc version of the driver:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Copyright 2004 MontaVista Software, Inc.
1362306a36Sopenharmony_ci * Matt Porter <mporter@kernel.crashing.org>
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * Copyright (c) 2005 Eugene Surovegin <ebs@ebshome.net>
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1862306a36Sopenharmony_ci#include <linux/of_address.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <asm/io.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "emac.h"
2362306a36Sopenharmony_ci#include "core.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciint tah_attach(struct platform_device *ofdev, int channel)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	struct tah_instance *dev = platform_get_drvdata(ofdev);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	mutex_lock(&dev->lock);
3062306a36Sopenharmony_ci	/* Reset has been done at probe() time... nothing else to do for now */
3162306a36Sopenharmony_ci	++dev->users;
3262306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_civoid tah_detach(struct platform_device *ofdev, int channel)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct tah_instance *dev = platform_get_drvdata(ofdev);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	mutex_lock(&dev->lock);
4262306a36Sopenharmony_ci	--dev->users;
4362306a36Sopenharmony_ci	mutex_unlock(&dev->lock);
4462306a36Sopenharmony_ci}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_civoid tah_reset(struct platform_device *ofdev)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct tah_instance *dev = platform_get_drvdata(ofdev);
4962306a36Sopenharmony_ci	struct tah_regs __iomem *p = dev->base;
5062306a36Sopenharmony_ci	int n;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* Reset TAH */
5362306a36Sopenharmony_ci	out_be32(&p->mr, TAH_MR_SR);
5462306a36Sopenharmony_ci	n = 100;
5562306a36Sopenharmony_ci	while ((in_be32(&p->mr) & TAH_MR_SR) && n)
5662306a36Sopenharmony_ci		--n;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (unlikely(!n))
5962306a36Sopenharmony_ci		printk(KERN_ERR "%pOF: reset timeout\n", ofdev->dev.of_node);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* 10KB TAH TX FIFO accommodates the max MTU of 9000 */
6262306a36Sopenharmony_ci	out_be32(&p->mr,
6362306a36Sopenharmony_ci		 TAH_MR_CVR | TAH_MR_ST_768 | TAH_MR_TFS_10KB | TAH_MR_DTFP |
6462306a36Sopenharmony_ci		 TAH_MR_DIG);
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ciint tah_get_regs_len(struct platform_device *ofdev)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	return sizeof(struct emac_ethtool_regs_subhdr) +
7062306a36Sopenharmony_ci		sizeof(struct tah_regs);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_civoid *tah_dump_regs(struct platform_device *ofdev, void *buf)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct tah_instance *dev = platform_get_drvdata(ofdev);
7662306a36Sopenharmony_ci	struct emac_ethtool_regs_subhdr *hdr = buf;
7762306a36Sopenharmony_ci	struct tah_regs *regs = (struct tah_regs *)(hdr + 1);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	hdr->version = 0;
8062306a36Sopenharmony_ci	hdr->index = 0; /* for now, are there chips with more than one
8162306a36Sopenharmony_ci			 * zmii ? if yes, then we'll add a cell_index
8262306a36Sopenharmony_ci			 * like we do for emac
8362306a36Sopenharmony_ci			 */
8462306a36Sopenharmony_ci	memcpy_fromio(regs, dev->base, sizeof(struct tah_regs));
8562306a36Sopenharmony_ci	return regs + 1;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int tah_probe(struct platform_device *ofdev)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct device_node *np = ofdev->dev.of_node;
9162306a36Sopenharmony_ci	struct tah_instance *dev;
9262306a36Sopenharmony_ci	struct resource regs;
9362306a36Sopenharmony_ci	int rc;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	rc = -ENOMEM;
9662306a36Sopenharmony_ci	dev = kzalloc(sizeof(struct tah_instance), GFP_KERNEL);
9762306a36Sopenharmony_ci	if (dev == NULL)
9862306a36Sopenharmony_ci		goto err_gone;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	mutex_init(&dev->lock);
10162306a36Sopenharmony_ci	dev->ofdev = ofdev;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	rc = -ENXIO;
10462306a36Sopenharmony_ci	if (of_address_to_resource(np, 0, &regs)) {
10562306a36Sopenharmony_ci		printk(KERN_ERR "%pOF: Can't get registers address\n", np);
10662306a36Sopenharmony_ci		goto err_free;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	rc = -ENOMEM;
11062306a36Sopenharmony_ci	dev->base = (struct tah_regs __iomem *)ioremap(regs.start,
11162306a36Sopenharmony_ci					       sizeof(struct tah_regs));
11262306a36Sopenharmony_ci	if (dev->base == NULL) {
11362306a36Sopenharmony_ci		printk(KERN_ERR "%pOF: Can't map device registers!\n", np);
11462306a36Sopenharmony_ci		goto err_free;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	platform_set_drvdata(ofdev, dev);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* Initialize TAH and enable IPv4 checksum verification, no TSO yet */
12062306a36Sopenharmony_ci	tah_reset(ofdev);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	printk(KERN_INFO "TAH %pOF initialized\n", ofdev->dev.of_node);
12362306a36Sopenharmony_ci	wmb();
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	return 0;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci err_free:
12862306a36Sopenharmony_ci	kfree(dev);
12962306a36Sopenharmony_ci err_gone:
13062306a36Sopenharmony_ci	return rc;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int tah_remove(struct platform_device *ofdev)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct tah_instance *dev = platform_get_drvdata(ofdev);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	WARN_ON(dev->users != 0);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	iounmap(dev->base);
14062306a36Sopenharmony_ci	kfree(dev);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic const struct of_device_id tah_match[] =
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	{
14862306a36Sopenharmony_ci		.compatible	= "ibm,tah",
14962306a36Sopenharmony_ci	},
15062306a36Sopenharmony_ci	/* For backward compat with old DT */
15162306a36Sopenharmony_ci	{
15262306a36Sopenharmony_ci		.type		= "tah",
15362306a36Sopenharmony_ci	},
15462306a36Sopenharmony_ci	{},
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic struct platform_driver tah_driver = {
15862306a36Sopenharmony_ci	.driver = {
15962306a36Sopenharmony_ci		.name = "emac-tah",
16062306a36Sopenharmony_ci		.of_match_table = tah_match,
16162306a36Sopenharmony_ci	},
16262306a36Sopenharmony_ci	.probe = tah_probe,
16362306a36Sopenharmony_ci	.remove = tah_remove,
16462306a36Sopenharmony_ci};
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ciint __init tah_init(void)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	return platform_driver_register(&tah_driver);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_civoid tah_exit(void)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	platform_driver_unregister(&tah_driver);
17462306a36Sopenharmony_ci}
175