162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * tsi108/109 device setup code
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Maintained by Roy Zang < tie-fei.zang@freescale.com >
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/stddef.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/major.h>
1362306a36Sopenharmony_ci#include <linux/delay.h>
1462306a36Sopenharmony_ci#include <linux/irq.h>
1562306a36Sopenharmony_ci#include <linux/export.h>
1662306a36Sopenharmony_ci#include <linux/device.h>
1762306a36Sopenharmony_ci#include <linux/etherdevice.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/of_address.h>
2062306a36Sopenharmony_ci#include <linux/of_irq.h>
2162306a36Sopenharmony_ci#include <linux/of_net.h>
2262306a36Sopenharmony_ci#include <asm/tsi108.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/atomic.h>
2562306a36Sopenharmony_ci#include <asm/io.h>
2662306a36Sopenharmony_ci#include <asm/irq.h>
2762306a36Sopenharmony_ci#include <mm/mmu_decl.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#undef DEBUG
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#ifdef DEBUG
3262306a36Sopenharmony_ci#define DBG(fmt...) do { printk(fmt); } while(0)
3362306a36Sopenharmony_ci#else
3462306a36Sopenharmony_ci#define DBG(fmt...) do { } while(0)
3562306a36Sopenharmony_ci#endif
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic phys_addr_t tsi108_csr_base = -1;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciphys_addr_t get_csrbase(void)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	struct device_node *tsi;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (tsi108_csr_base != -1)
4462306a36Sopenharmony_ci		return tsi108_csr_base;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	tsi = of_find_node_by_type(NULL, "tsi-bridge");
4762306a36Sopenharmony_ci	if (tsi) {
4862306a36Sopenharmony_ci		struct resource res;
4962306a36Sopenharmony_ci		of_address_to_resource(tsi, 0, &res);
5062306a36Sopenharmony_ci		tsi108_csr_base = res.start;
5162306a36Sopenharmony_ci		of_node_put(tsi);
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci	return tsi108_csr_base;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ciEXPORT_SYMBOL(get_csrbase);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ciu32 get_vir_csrbase(void)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	return (u32) (ioremap(get_csrbase(), 0x10000));
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ciEXPORT_SYMBOL(get_vir_csrbase);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int __init tsi108_eth_of_init(void)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct device_node *np;
6662306a36Sopenharmony_ci	unsigned int i = 0;
6762306a36Sopenharmony_ci	struct platform_device *tsi_eth_dev;
6862306a36Sopenharmony_ci	struct resource res;
6962306a36Sopenharmony_ci	int ret;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	for_each_compatible_node(np, "network", "tsi108-ethernet") {
7262306a36Sopenharmony_ci		struct resource r[2];
7362306a36Sopenharmony_ci		struct device_node *phy, *mdio;
7462306a36Sopenharmony_ci		hw_info tsi_eth_data;
7562306a36Sopenharmony_ci		const unsigned int *phy_id;
7662306a36Sopenharmony_ci		const phandle *ph;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		memset(r, 0, sizeof(r));
7962306a36Sopenharmony_ci		memset(&tsi_eth_data, 0, sizeof(tsi_eth_data));
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci		ret = of_address_to_resource(np, 0, &r[0]);
8262306a36Sopenharmony_ci		DBG("%s: name:start->end = %s:%pR\n",
8362306a36Sopenharmony_ci		    __func__, r[0].name, &r[0]);
8462306a36Sopenharmony_ci		if (ret)
8562306a36Sopenharmony_ci			goto err;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci		r[1].name = "tx";
8862306a36Sopenharmony_ci		r[1].start = irq_of_parse_and_map(np, 0);
8962306a36Sopenharmony_ci		r[1].end = irq_of_parse_and_map(np, 0);
9062306a36Sopenharmony_ci		r[1].flags = IORESOURCE_IRQ;
9162306a36Sopenharmony_ci		DBG("%s: name:start->end = %s:%pR\n",
9262306a36Sopenharmony_ci			__func__, r[1].name, &r[1]);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		tsi_eth_dev =
9562306a36Sopenharmony_ci		    platform_device_register_simple("tsi-ethernet", i++, &r[0],
9662306a36Sopenharmony_ci						    1);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		if (IS_ERR(tsi_eth_dev)) {
9962306a36Sopenharmony_ci			ret = PTR_ERR(tsi_eth_dev);
10062306a36Sopenharmony_ci			goto err;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		of_get_mac_address(np, tsi_eth_data.mac_addr);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		ph = of_get_property(np, "mdio-handle", NULL);
10662306a36Sopenharmony_ci		mdio = of_find_node_by_phandle(*ph);
10762306a36Sopenharmony_ci		ret = of_address_to_resource(mdio, 0, &res);
10862306a36Sopenharmony_ci		of_node_put(mdio);
10962306a36Sopenharmony_ci		if (ret)
11062306a36Sopenharmony_ci			goto unreg;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		ph = of_get_property(np, "phy-handle", NULL);
11362306a36Sopenharmony_ci		phy = of_find_node_by_phandle(*ph);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		if (phy == NULL) {
11662306a36Sopenharmony_ci			ret = -ENODEV;
11762306a36Sopenharmony_ci			goto unreg;
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		phy_id = of_get_property(phy, "reg", NULL);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		tsi_eth_data.regs = r[0].start;
12362306a36Sopenharmony_ci		tsi_eth_data.phyregs = res.start;
12462306a36Sopenharmony_ci		tsi_eth_data.phy = *phy_id;
12562306a36Sopenharmony_ci		tsi_eth_data.irq_num = irq_of_parse_and_map(np, 0);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci		/* Some boards with the TSI108 bridge (e.g. Holly)
12862306a36Sopenharmony_ci		 * have a miswiring of the ethernet PHYs which
12962306a36Sopenharmony_ci		 * requires a workaround.  The special
13062306a36Sopenharmony_ci		 * "txc-rxc-delay-disable" property enables this
13162306a36Sopenharmony_ci		 * workaround.  FIXME: Need to port the tsi108_eth
13262306a36Sopenharmony_ci		 * driver itself to phylib and use a non-misleading
13362306a36Sopenharmony_ci		 * name for the workaround flag - it's not actually to
13462306a36Sopenharmony_ci		 * do with the model of PHY in use */
13562306a36Sopenharmony_ci		if (of_property_read_bool(phy, "txc-rxc-delay-disable"))
13662306a36Sopenharmony_ci			tsi_eth_data.phy_type = TSI108_PHY_BCM54XX;
13762306a36Sopenharmony_ci		of_node_put(phy);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		ret =
14062306a36Sopenharmony_ci		    platform_device_add_data(tsi_eth_dev, &tsi_eth_data,
14162306a36Sopenharmony_ci					     sizeof(hw_info));
14262306a36Sopenharmony_ci		if (ret)
14362306a36Sopenharmony_ci			goto unreg;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ciunreg:
14762306a36Sopenharmony_ci	platform_device_unregister(tsi_eth_dev);
14862306a36Sopenharmony_cierr:
14962306a36Sopenharmony_ci	of_node_put(np);
15062306a36Sopenharmony_ci	return ret;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ciarch_initcall(tsi108_eth_of_init);
154