1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * tsi108/109 device setup code |
4 | * |
5 | * Maintained by Roy Zang < tie-fei.zang@freescale.com > |
6 | */ |
7 | |
8 | #include <linux/stddef.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/init.h> |
11 | #include <linux/errno.h> |
12 | #include <linux/major.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/irq.h> |
15 | #include <linux/export.h> |
16 | #include <linux/device.h> |
17 | #include <linux/etherdevice.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/of_address.h> |
20 | #include <linux/of_irq.h> |
21 | #include <linux/of_net.h> |
22 | #include <asm/tsi108.h> |
23 | |
24 | #include <linux/atomic.h> |
25 | #include <asm/io.h> |
26 | #include <asm/irq.h> |
27 | #include <mm/mmu_decl.h> |
28 | |
29 | #undef DEBUG |
30 | |
31 | #ifdef DEBUG |
32 | #define DBG(fmt...) do { printk(fmt); } while(0) |
33 | #else |
34 | #define DBG(fmt...) do { } while(0) |
35 | #endif |
36 | |
37 | static phys_addr_t tsi108_csr_base = -1; |
38 | |
39 | phys_addr_t get_csrbase(void) |
40 | { |
41 | struct device_node *tsi; |
42 | |
43 | if (tsi108_csr_base != -1) |
44 | return tsi108_csr_base; |
45 | |
46 | tsi = of_find_node_by_type(NULL, type: "tsi-bridge" ); |
47 | if (tsi) { |
48 | struct resource res; |
49 | of_address_to_resource(dev: tsi, index: 0, r: &res); |
50 | tsi108_csr_base = res.start; |
51 | of_node_put(node: tsi); |
52 | } |
53 | return tsi108_csr_base; |
54 | } |
55 | EXPORT_SYMBOL(get_csrbase); |
56 | |
57 | u32 get_vir_csrbase(void) |
58 | { |
59 | return (u32) (ioremap(offset: get_csrbase(), size: 0x10000)); |
60 | } |
61 | EXPORT_SYMBOL(get_vir_csrbase); |
62 | |
63 | static int __init tsi108_eth_of_init(void) |
64 | { |
65 | struct device_node *np; |
66 | unsigned int i = 0; |
67 | struct platform_device *tsi_eth_dev; |
68 | struct resource res; |
69 | int ret; |
70 | |
71 | for_each_compatible_node(np, "network" , "tsi108-ethernet" ) { |
72 | struct resource r[2]; |
73 | struct device_node *phy, *mdio; |
74 | hw_info tsi_eth_data; |
75 | const unsigned int *phy_id; |
76 | const phandle *ph; |
77 | |
78 | memset(r, 0, sizeof(r)); |
79 | memset(&tsi_eth_data, 0, sizeof(tsi_eth_data)); |
80 | |
81 | ret = of_address_to_resource(dev: np, index: 0, r: &r[0]); |
82 | DBG("%s: name:start->end = %s:%pR\n" , |
83 | __func__, r[0].name, &r[0]); |
84 | if (ret) |
85 | goto err; |
86 | |
87 | r[1].name = "tx" ; |
88 | r[1].start = irq_of_parse_and_map(node: np, index: 0); |
89 | r[1].end = irq_of_parse_and_map(node: np, index: 0); |
90 | r[1].flags = IORESOURCE_IRQ; |
91 | DBG("%s: name:start->end = %s:%pR\n" , |
92 | __func__, r[1].name, &r[1]); |
93 | |
94 | tsi_eth_dev = |
95 | platform_device_register_simple(name: "tsi-ethernet" , id: i++, res: &r[0], |
96 | num: 1); |
97 | |
98 | if (IS_ERR(ptr: tsi_eth_dev)) { |
99 | ret = PTR_ERR(ptr: tsi_eth_dev); |
100 | goto err; |
101 | } |
102 | |
103 | of_get_mac_address(np, mac: tsi_eth_data.mac_addr); |
104 | |
105 | ph = of_get_property(node: np, name: "mdio-handle" , NULL); |
106 | mdio = of_find_node_by_phandle(handle: *ph); |
107 | ret = of_address_to_resource(dev: mdio, index: 0, r: &res); |
108 | of_node_put(node: mdio); |
109 | if (ret) |
110 | goto unreg; |
111 | |
112 | ph = of_get_property(node: np, name: "phy-handle" , NULL); |
113 | phy = of_find_node_by_phandle(handle: *ph); |
114 | |
115 | if (phy == NULL) { |
116 | ret = -ENODEV; |
117 | goto unreg; |
118 | } |
119 | |
120 | phy_id = of_get_property(node: phy, name: "reg" , NULL); |
121 | |
122 | tsi_eth_data.regs = r[0].start; |
123 | tsi_eth_data.phyregs = res.start; |
124 | tsi_eth_data.phy = *phy_id; |
125 | tsi_eth_data.irq_num = irq_of_parse_and_map(node: np, index: 0); |
126 | |
127 | /* Some boards with the TSI108 bridge (e.g. Holly) |
128 | * have a miswiring of the ethernet PHYs which |
129 | * requires a workaround. The special |
130 | * "txc-rxc-delay-disable" property enables this |
131 | * workaround. FIXME: Need to port the tsi108_eth |
132 | * driver itself to phylib and use a non-misleading |
133 | * name for the workaround flag - it's not actually to |
134 | * do with the model of PHY in use */ |
135 | if (of_property_read_bool(np: phy, propname: "txc-rxc-delay-disable" )) |
136 | tsi_eth_data.phy_type = TSI108_PHY_BCM54XX; |
137 | of_node_put(node: phy); |
138 | |
139 | ret = |
140 | platform_device_add_data(pdev: tsi_eth_dev, data: &tsi_eth_data, |
141 | size: sizeof(hw_info)); |
142 | if (ret) |
143 | goto unreg; |
144 | } |
145 | return 0; |
146 | unreg: |
147 | platform_device_unregister(tsi_eth_dev); |
148 | err: |
149 | of_node_put(node: np); |
150 | return ret; |
151 | } |
152 | |
153 | arch_initcall(tsi108_eth_of_init); |
154 | |