xref: /linux/drivers/net/ethernet/ti/icssm/icssm_prueth.c (revision 511f6c1ae093c7045742299d29eba71925709a71)
1*511f6c1aSRoger Quadros // SPDX-License-Identifier: GPL-2.0
2*511f6c1aSRoger Quadros 
3*511f6c1aSRoger Quadros /* Texas Instruments ICSSM Ethernet Driver
4*511f6c1aSRoger Quadros  *
5*511f6c1aSRoger Quadros  * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/
6*511f6c1aSRoger Quadros  *
7*511f6c1aSRoger Quadros  */
8*511f6c1aSRoger Quadros 
9*511f6c1aSRoger Quadros #include <linux/etherdevice.h>
10*511f6c1aSRoger Quadros #include <linux/genalloc.h>
11*511f6c1aSRoger Quadros #include <linux/if_bridge.h>
12*511f6c1aSRoger Quadros #include <linux/if_hsr.h>
13*511f6c1aSRoger Quadros #include <linux/if_vlan.h>
14*511f6c1aSRoger Quadros #include <linux/interrupt.h>
15*511f6c1aSRoger Quadros #include <linux/kernel.h>
16*511f6c1aSRoger Quadros #include <linux/mfd/syscon.h>
17*511f6c1aSRoger Quadros #include <linux/module.h>
18*511f6c1aSRoger Quadros #include <linux/net_tstamp.h>
19*511f6c1aSRoger Quadros #include <linux/of.h>
20*511f6c1aSRoger Quadros #include <linux/of_irq.h>
21*511f6c1aSRoger Quadros #include <linux/of_mdio.h>
22*511f6c1aSRoger Quadros #include <linux/of_net.h>
23*511f6c1aSRoger Quadros #include <linux/platform_device.h>
24*511f6c1aSRoger Quadros #include <linux/phy.h>
25*511f6c1aSRoger Quadros #include <linux/remoteproc/pruss.h>
26*511f6c1aSRoger Quadros #include <linux/ptp_classify.h>
27*511f6c1aSRoger Quadros #include <linux/regmap.h>
28*511f6c1aSRoger Quadros #include <linux/remoteproc.h>
29*511f6c1aSRoger Quadros #include <net/pkt_cls.h>
30*511f6c1aSRoger Quadros 
31*511f6c1aSRoger Quadros #include "icssm_prueth.h"
32*511f6c1aSRoger Quadros 
33*511f6c1aSRoger Quadros /* called back by PHY layer if there is change in link state of hw port*/
34*511f6c1aSRoger Quadros static void icssm_emac_adjust_link(struct net_device *ndev)
35*511f6c1aSRoger Quadros {
36*511f6c1aSRoger Quadros 	struct prueth_emac *emac = netdev_priv(ndev);
37*511f6c1aSRoger Quadros 	struct phy_device *phydev = emac->phydev;
38*511f6c1aSRoger Quadros 	bool new_state = false;
39*511f6c1aSRoger Quadros 	unsigned long flags;
40*511f6c1aSRoger Quadros 
41*511f6c1aSRoger Quadros 	spin_lock_irqsave(&emac->lock, flags);
42*511f6c1aSRoger Quadros 
43*511f6c1aSRoger Quadros 	if (phydev->link) {
44*511f6c1aSRoger Quadros 		/* check the mode of operation */
45*511f6c1aSRoger Quadros 		if (phydev->duplex != emac->duplex) {
46*511f6c1aSRoger Quadros 			new_state = true;
47*511f6c1aSRoger Quadros 			emac->duplex = phydev->duplex;
48*511f6c1aSRoger Quadros 		}
49*511f6c1aSRoger Quadros 		if (phydev->speed != emac->speed) {
50*511f6c1aSRoger Quadros 			new_state = true;
51*511f6c1aSRoger Quadros 			emac->speed = phydev->speed;
52*511f6c1aSRoger Quadros 		}
53*511f6c1aSRoger Quadros 		if (!emac->link) {
54*511f6c1aSRoger Quadros 			new_state = true;
55*511f6c1aSRoger Quadros 			emac->link = 1;
56*511f6c1aSRoger Quadros 		}
57*511f6c1aSRoger Quadros 	} else if (emac->link) {
58*511f6c1aSRoger Quadros 		new_state = true;
59*511f6c1aSRoger Quadros 		emac->link = 0;
60*511f6c1aSRoger Quadros 	}
61*511f6c1aSRoger Quadros 
62*511f6c1aSRoger Quadros 	if (new_state)
63*511f6c1aSRoger Quadros 		phy_print_status(phydev);
64*511f6c1aSRoger Quadros 
65*511f6c1aSRoger Quadros 	if (emac->link) {
66*511f6c1aSRoger Quadros 	       /* reactivate the transmit queue if it is stopped */
67*511f6c1aSRoger Quadros 		if (netif_running(ndev) && netif_queue_stopped(ndev))
68*511f6c1aSRoger Quadros 			netif_wake_queue(ndev);
69*511f6c1aSRoger Quadros 	} else {
70*511f6c1aSRoger Quadros 		if (!netif_queue_stopped(ndev))
71*511f6c1aSRoger Quadros 			netif_stop_queue(ndev);
72*511f6c1aSRoger Quadros 	}
73*511f6c1aSRoger Quadros 
74*511f6c1aSRoger Quadros 	spin_unlock_irqrestore(&emac->lock, flags);
75*511f6c1aSRoger Quadros }
76*511f6c1aSRoger Quadros 
77*511f6c1aSRoger Quadros static int icssm_emac_set_boot_pru(struct prueth_emac *emac,
78*511f6c1aSRoger Quadros 				   struct net_device *ndev)
79*511f6c1aSRoger Quadros {
80*511f6c1aSRoger Quadros 	const struct prueth_firmware *pru_firmwares;
81*511f6c1aSRoger Quadros 	struct prueth *prueth = emac->prueth;
82*511f6c1aSRoger Quadros 	const char *fw_name;
83*511f6c1aSRoger Quadros 	int ret;
84*511f6c1aSRoger Quadros 
85*511f6c1aSRoger Quadros 	pru_firmwares = &prueth->fw_data->fw_pru[emac->port_id - 1];
86*511f6c1aSRoger Quadros 	fw_name = pru_firmwares->fw_name[prueth->eth_type];
87*511f6c1aSRoger Quadros 	if (!fw_name) {
88*511f6c1aSRoger Quadros 		netdev_err(ndev, "eth_type %d not supported\n",
89*511f6c1aSRoger Quadros 			   prueth->eth_type);
90*511f6c1aSRoger Quadros 		return -ENODEV;
91*511f6c1aSRoger Quadros 	}
92*511f6c1aSRoger Quadros 
93*511f6c1aSRoger Quadros 	ret = rproc_set_firmware(emac->pru, fw_name);
94*511f6c1aSRoger Quadros 	if (ret) {
95*511f6c1aSRoger Quadros 		netdev_err(ndev, "failed to set %s firmware: %d\n",
96*511f6c1aSRoger Quadros 			   fw_name, ret);
97*511f6c1aSRoger Quadros 		return ret;
98*511f6c1aSRoger Quadros 	}
99*511f6c1aSRoger Quadros 
100*511f6c1aSRoger Quadros 	ret = rproc_boot(emac->pru);
101*511f6c1aSRoger Quadros 	if (ret) {
102*511f6c1aSRoger Quadros 		netdev_err(ndev, "failed to boot %s firmware: %d\n",
103*511f6c1aSRoger Quadros 			   fw_name, ret);
104*511f6c1aSRoger Quadros 		return ret;
105*511f6c1aSRoger Quadros 	}
106*511f6c1aSRoger Quadros 
107*511f6c1aSRoger Quadros 	return ret;
108*511f6c1aSRoger Quadros }
109*511f6c1aSRoger Quadros 
110*511f6c1aSRoger Quadros /**
111*511f6c1aSRoger Quadros  * icssm_emac_ndo_open - EMAC device open
112*511f6c1aSRoger Quadros  * @ndev: network adapter device
113*511f6c1aSRoger Quadros  *
114*511f6c1aSRoger Quadros  * Called when system wants to start the interface.
115*511f6c1aSRoger Quadros  *
116*511f6c1aSRoger Quadros  * Return: 0 for a successful open, or appropriate error code
117*511f6c1aSRoger Quadros  */
118*511f6c1aSRoger Quadros static int icssm_emac_ndo_open(struct net_device *ndev)
119*511f6c1aSRoger Quadros {
120*511f6c1aSRoger Quadros 	struct prueth_emac *emac = netdev_priv(ndev);
121*511f6c1aSRoger Quadros 	int ret;
122*511f6c1aSRoger Quadros 
123*511f6c1aSRoger Quadros 	ret = icssm_emac_set_boot_pru(emac, ndev);
124*511f6c1aSRoger Quadros 	if (ret)
125*511f6c1aSRoger Quadros 		return ret;
126*511f6c1aSRoger Quadros 
127*511f6c1aSRoger Quadros 	/* start PHY */
128*511f6c1aSRoger Quadros 	phy_start(emac->phydev);
129*511f6c1aSRoger Quadros 
130*511f6c1aSRoger Quadros 	return 0;
131*511f6c1aSRoger Quadros }
132*511f6c1aSRoger Quadros 
133*511f6c1aSRoger Quadros /**
134*511f6c1aSRoger Quadros  * icssm_emac_ndo_stop - EMAC device stop
135*511f6c1aSRoger Quadros  * @ndev: network adapter device
136*511f6c1aSRoger Quadros  *
137*511f6c1aSRoger Quadros  * Called when system wants to stop or down the interface.
138*511f6c1aSRoger Quadros  *
139*511f6c1aSRoger Quadros  * Return: Always 0 (Success)
140*511f6c1aSRoger Quadros  */
141*511f6c1aSRoger Quadros static int icssm_emac_ndo_stop(struct net_device *ndev)
142*511f6c1aSRoger Quadros {
143*511f6c1aSRoger Quadros 	struct prueth_emac *emac = netdev_priv(ndev);
144*511f6c1aSRoger Quadros 
145*511f6c1aSRoger Quadros 	/* stop PHY */
146*511f6c1aSRoger Quadros 	phy_stop(emac->phydev);
147*511f6c1aSRoger Quadros 
148*511f6c1aSRoger Quadros 	rproc_shutdown(emac->pru);
149*511f6c1aSRoger Quadros 
150*511f6c1aSRoger Quadros 	return 0;
151*511f6c1aSRoger Quadros }
152*511f6c1aSRoger Quadros 
153*511f6c1aSRoger Quadros static const struct net_device_ops emac_netdev_ops = {
154*511f6c1aSRoger Quadros 	.ndo_open = icssm_emac_ndo_open,
155*511f6c1aSRoger Quadros 	.ndo_stop = icssm_emac_ndo_stop,
156*511f6c1aSRoger Quadros };
157*511f6c1aSRoger Quadros 
158*511f6c1aSRoger Quadros /* get emac_port corresponding to eth_node name */
159*511f6c1aSRoger Quadros static int icssm_prueth_node_port(struct device_node *eth_node)
160*511f6c1aSRoger Quadros {
161*511f6c1aSRoger Quadros 	u32 port_id;
162*511f6c1aSRoger Quadros 	int ret;
163*511f6c1aSRoger Quadros 
164*511f6c1aSRoger Quadros 	ret = of_property_read_u32(eth_node, "reg", &port_id);
165*511f6c1aSRoger Quadros 	if (ret)
166*511f6c1aSRoger Quadros 		return ret;
167*511f6c1aSRoger Quadros 
168*511f6c1aSRoger Quadros 	if (port_id == 0)
169*511f6c1aSRoger Quadros 		return PRUETH_PORT_MII0;
170*511f6c1aSRoger Quadros 	else if (port_id == 1)
171*511f6c1aSRoger Quadros 		return PRUETH_PORT_MII1;
172*511f6c1aSRoger Quadros 	else
173*511f6c1aSRoger Quadros 		return PRUETH_PORT_INVALID;
174*511f6c1aSRoger Quadros }
175*511f6c1aSRoger Quadros 
176*511f6c1aSRoger Quadros /* get MAC instance corresponding to eth_node name */
177*511f6c1aSRoger Quadros static int icssm_prueth_node_mac(struct device_node *eth_node)
178*511f6c1aSRoger Quadros {
179*511f6c1aSRoger Quadros 	u32 port_id;
180*511f6c1aSRoger Quadros 	int ret;
181*511f6c1aSRoger Quadros 
182*511f6c1aSRoger Quadros 	ret = of_property_read_u32(eth_node, "reg", &port_id);
183*511f6c1aSRoger Quadros 	if (ret)
184*511f6c1aSRoger Quadros 		return ret;
185*511f6c1aSRoger Quadros 
186*511f6c1aSRoger Quadros 	if (port_id == 0)
187*511f6c1aSRoger Quadros 		return PRUETH_MAC0;
188*511f6c1aSRoger Quadros 	else if (port_id == 1)
189*511f6c1aSRoger Quadros 		return PRUETH_MAC1;
190*511f6c1aSRoger Quadros 	else
191*511f6c1aSRoger Quadros 		return PRUETH_MAC_INVALID;
192*511f6c1aSRoger Quadros }
193*511f6c1aSRoger Quadros 
194*511f6c1aSRoger Quadros static int icssm_prueth_netdev_init(struct prueth *prueth,
195*511f6c1aSRoger Quadros 				    struct device_node *eth_node)
196*511f6c1aSRoger Quadros {
197*511f6c1aSRoger Quadros 	struct prueth_emac *emac;
198*511f6c1aSRoger Quadros 	struct net_device *ndev;
199*511f6c1aSRoger Quadros 	enum prueth_port port;
200*511f6c1aSRoger Quadros 	enum prueth_mac mac;
201*511f6c1aSRoger Quadros 	int ret;
202*511f6c1aSRoger Quadros 
203*511f6c1aSRoger Quadros 	port = icssm_prueth_node_port(eth_node);
204*511f6c1aSRoger Quadros 	if (port == PRUETH_PORT_INVALID)
205*511f6c1aSRoger Quadros 		return -EINVAL;
206*511f6c1aSRoger Quadros 
207*511f6c1aSRoger Quadros 	mac = icssm_prueth_node_mac(eth_node);
208*511f6c1aSRoger Quadros 	if (mac == PRUETH_MAC_INVALID)
209*511f6c1aSRoger Quadros 		return -EINVAL;
210*511f6c1aSRoger Quadros 
211*511f6c1aSRoger Quadros 	ndev = devm_alloc_etherdev(prueth->dev, sizeof(*emac));
212*511f6c1aSRoger Quadros 	if (!ndev)
213*511f6c1aSRoger Quadros 		return -ENOMEM;
214*511f6c1aSRoger Quadros 
215*511f6c1aSRoger Quadros 	SET_NETDEV_DEV(ndev, prueth->dev);
216*511f6c1aSRoger Quadros 	emac = netdev_priv(ndev);
217*511f6c1aSRoger Quadros 	prueth->emac[mac] = emac;
218*511f6c1aSRoger Quadros 	emac->prueth = prueth;
219*511f6c1aSRoger Quadros 	emac->ndev = ndev;
220*511f6c1aSRoger Quadros 	emac->port_id = port;
221*511f6c1aSRoger Quadros 
222*511f6c1aSRoger Quadros 	/* by default eth_type is EMAC */
223*511f6c1aSRoger Quadros 	switch (port) {
224*511f6c1aSRoger Quadros 	case PRUETH_PORT_MII0:
225*511f6c1aSRoger Quadros 		emac->pru = prueth->pru0;
226*511f6c1aSRoger Quadros 		break;
227*511f6c1aSRoger Quadros 	case PRUETH_PORT_MII1:
228*511f6c1aSRoger Quadros 		emac->pru = prueth->pru1;
229*511f6c1aSRoger Quadros 		break;
230*511f6c1aSRoger Quadros 	default:
231*511f6c1aSRoger Quadros 		return -EINVAL;
232*511f6c1aSRoger Quadros 	}
233*511f6c1aSRoger Quadros 	/* get mac address from DT and set private and netdev addr */
234*511f6c1aSRoger Quadros 	ret = of_get_ethdev_address(eth_node, ndev);
235*511f6c1aSRoger Quadros 	if (!is_valid_ether_addr(ndev->dev_addr)) {
236*511f6c1aSRoger Quadros 		eth_hw_addr_random(ndev);
237*511f6c1aSRoger Quadros 		dev_warn(prueth->dev, "port %d: using random MAC addr: %pM\n",
238*511f6c1aSRoger Quadros 			 port, ndev->dev_addr);
239*511f6c1aSRoger Quadros 	}
240*511f6c1aSRoger Quadros 	ether_addr_copy(emac->mac_addr, ndev->dev_addr);
241*511f6c1aSRoger Quadros 
242*511f6c1aSRoger Quadros 	/* connect PHY */
243*511f6c1aSRoger Quadros 	emac->phydev = of_phy_get_and_connect(ndev, eth_node,
244*511f6c1aSRoger Quadros 					      icssm_emac_adjust_link);
245*511f6c1aSRoger Quadros 	if (!emac->phydev) {
246*511f6c1aSRoger Quadros 		dev_dbg(prueth->dev, "PHY connection failed\n");
247*511f6c1aSRoger Quadros 		ret = -ENODEV;
248*511f6c1aSRoger Quadros 		goto free;
249*511f6c1aSRoger Quadros 	}
250*511f6c1aSRoger Quadros 
251*511f6c1aSRoger Quadros 	/* remove unsupported modes */
252*511f6c1aSRoger Quadros 	phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT);
253*511f6c1aSRoger Quadros 
254*511f6c1aSRoger Quadros 	phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT);
255*511f6c1aSRoger Quadros 	phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT);
256*511f6c1aSRoger Quadros 
257*511f6c1aSRoger Quadros 	phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT);
258*511f6c1aSRoger Quadros 	phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT);
259*511f6c1aSRoger Quadros 
260*511f6c1aSRoger Quadros 	ndev->dev.of_node = eth_node;
261*511f6c1aSRoger Quadros 	ndev->netdev_ops = &emac_netdev_ops;
262*511f6c1aSRoger Quadros 
263*511f6c1aSRoger Quadros 	return 0;
264*511f6c1aSRoger Quadros free:
265*511f6c1aSRoger Quadros 	emac->ndev = NULL;
266*511f6c1aSRoger Quadros 	prueth->emac[mac] = NULL;
267*511f6c1aSRoger Quadros 
268*511f6c1aSRoger Quadros 	return ret;
269*511f6c1aSRoger Quadros }
270*511f6c1aSRoger Quadros 
271*511f6c1aSRoger Quadros static void icssm_prueth_netdev_exit(struct prueth *prueth,
272*511f6c1aSRoger Quadros 				     struct device_node *eth_node)
273*511f6c1aSRoger Quadros {
274*511f6c1aSRoger Quadros 	struct prueth_emac *emac;
275*511f6c1aSRoger Quadros 	enum prueth_mac mac;
276*511f6c1aSRoger Quadros 
277*511f6c1aSRoger Quadros 	mac = icssm_prueth_node_mac(eth_node);
278*511f6c1aSRoger Quadros 	if (mac == PRUETH_MAC_INVALID)
279*511f6c1aSRoger Quadros 		return;
280*511f6c1aSRoger Quadros 
281*511f6c1aSRoger Quadros 	emac = prueth->emac[mac];
282*511f6c1aSRoger Quadros 	if (!emac)
283*511f6c1aSRoger Quadros 		return;
284*511f6c1aSRoger Quadros 
285*511f6c1aSRoger Quadros 	phy_disconnect(emac->phydev);
286*511f6c1aSRoger Quadros 
287*511f6c1aSRoger Quadros 	prueth->emac[mac] = NULL;
288*511f6c1aSRoger Quadros }
289*511f6c1aSRoger Quadros 
290*511f6c1aSRoger Quadros static int icssm_prueth_probe(struct platform_device *pdev)
291*511f6c1aSRoger Quadros {
292*511f6c1aSRoger Quadros 	struct device_node *eth0_node = NULL, *eth1_node = NULL;
293*511f6c1aSRoger Quadros 	struct device_node *eth_node, *eth_ports_node;
294*511f6c1aSRoger Quadros 	enum pruss_pru_id pruss_id0, pruss_id1;
295*511f6c1aSRoger Quadros 	struct device *dev = &pdev->dev;
296*511f6c1aSRoger Quadros 	struct device_node *np;
297*511f6c1aSRoger Quadros 	struct prueth *prueth;
298*511f6c1aSRoger Quadros 	int i, ret;
299*511f6c1aSRoger Quadros 
300*511f6c1aSRoger Quadros 	np = dev->of_node;
301*511f6c1aSRoger Quadros 	if (!np)
302*511f6c1aSRoger Quadros 		return -ENODEV; /* we don't support non DT */
303*511f6c1aSRoger Quadros 
304*511f6c1aSRoger Quadros 	prueth = devm_kzalloc(dev, sizeof(*prueth), GFP_KERNEL);
305*511f6c1aSRoger Quadros 	if (!prueth)
306*511f6c1aSRoger Quadros 		return -ENOMEM;
307*511f6c1aSRoger Quadros 
308*511f6c1aSRoger Quadros 	platform_set_drvdata(pdev, prueth);
309*511f6c1aSRoger Quadros 	prueth->dev = dev;
310*511f6c1aSRoger Quadros 	prueth->fw_data = device_get_match_data(dev);
311*511f6c1aSRoger Quadros 
312*511f6c1aSRoger Quadros 	eth_ports_node = of_get_child_by_name(np, "ethernet-ports");
313*511f6c1aSRoger Quadros 	if (!eth_ports_node)
314*511f6c1aSRoger Quadros 		return -ENOENT;
315*511f6c1aSRoger Quadros 
316*511f6c1aSRoger Quadros 	for_each_child_of_node(eth_ports_node, eth_node) {
317*511f6c1aSRoger Quadros 		u32 reg;
318*511f6c1aSRoger Quadros 
319*511f6c1aSRoger Quadros 		if (strcmp(eth_node->name, "ethernet-port"))
320*511f6c1aSRoger Quadros 			continue;
321*511f6c1aSRoger Quadros 		ret = of_property_read_u32(eth_node, "reg", &reg);
322*511f6c1aSRoger Quadros 		if (ret < 0) {
323*511f6c1aSRoger Quadros 			dev_err(dev, "%pOF error reading port_id %d\n",
324*511f6c1aSRoger Quadros 				eth_node, ret);
325*511f6c1aSRoger Quadros 			of_node_put(eth_node);
326*511f6c1aSRoger Quadros 			return ret;
327*511f6c1aSRoger Quadros 		}
328*511f6c1aSRoger Quadros 
329*511f6c1aSRoger Quadros 		of_node_get(eth_node);
330*511f6c1aSRoger Quadros 
331*511f6c1aSRoger Quadros 		if (reg == 0 && !eth0_node) {
332*511f6c1aSRoger Quadros 			eth0_node = eth_node;
333*511f6c1aSRoger Quadros 			if (!of_device_is_available(eth0_node)) {
334*511f6c1aSRoger Quadros 				of_node_put(eth0_node);
335*511f6c1aSRoger Quadros 				eth0_node = NULL;
336*511f6c1aSRoger Quadros 			}
337*511f6c1aSRoger Quadros 		} else if (reg == 1 && !eth1_node) {
338*511f6c1aSRoger Quadros 			eth1_node = eth_node;
339*511f6c1aSRoger Quadros 			if (!of_device_is_available(eth1_node)) {
340*511f6c1aSRoger Quadros 				of_node_put(eth1_node);
341*511f6c1aSRoger Quadros 				eth1_node = NULL;
342*511f6c1aSRoger Quadros 			}
343*511f6c1aSRoger Quadros 		} else {
344*511f6c1aSRoger Quadros 			if (reg == 0 || reg == 1)
345*511f6c1aSRoger Quadros 				dev_err(dev, "duplicate port reg value: %d\n",
346*511f6c1aSRoger Quadros 					reg);
347*511f6c1aSRoger Quadros 			else
348*511f6c1aSRoger Quadros 				dev_err(dev, "invalid port reg value: %d\n",
349*511f6c1aSRoger Quadros 					reg);
350*511f6c1aSRoger Quadros 
351*511f6c1aSRoger Quadros 			of_node_put(eth_node);
352*511f6c1aSRoger Quadros 		}
353*511f6c1aSRoger Quadros 	}
354*511f6c1aSRoger Quadros 
355*511f6c1aSRoger Quadros 	of_node_put(eth_ports_node);
356*511f6c1aSRoger Quadros 
357*511f6c1aSRoger Quadros 	/* At least one node must be present and available else we fail */
358*511f6c1aSRoger Quadros 	if (!eth0_node && !eth1_node) {
359*511f6c1aSRoger Quadros 		dev_err(dev, "neither port0 nor port1 node available\n");
360*511f6c1aSRoger Quadros 		return -ENODEV;
361*511f6c1aSRoger Quadros 	}
362*511f6c1aSRoger Quadros 
363*511f6c1aSRoger Quadros 	prueth->eth_node[PRUETH_MAC0] = eth0_node;
364*511f6c1aSRoger Quadros 	prueth->eth_node[PRUETH_MAC1] = eth1_node;
365*511f6c1aSRoger Quadros 
366*511f6c1aSRoger Quadros 	if (eth0_node) {
367*511f6c1aSRoger Quadros 		prueth->pru0 = pru_rproc_get(np, 0, &pruss_id0);
368*511f6c1aSRoger Quadros 		if (IS_ERR(prueth->pru0)) {
369*511f6c1aSRoger Quadros 			ret = PTR_ERR(prueth->pru0);
370*511f6c1aSRoger Quadros 			dev_err_probe(dev, ret, "unable to get PRU0");
371*511f6c1aSRoger Quadros 			goto put_pru;
372*511f6c1aSRoger Quadros 		}
373*511f6c1aSRoger Quadros 	}
374*511f6c1aSRoger Quadros 
375*511f6c1aSRoger Quadros 	if (eth1_node) {
376*511f6c1aSRoger Quadros 		prueth->pru1 = pru_rproc_get(np, 1, &pruss_id1);
377*511f6c1aSRoger Quadros 		if (IS_ERR(prueth->pru1)) {
378*511f6c1aSRoger Quadros 			ret = PTR_ERR(prueth->pru1);
379*511f6c1aSRoger Quadros 			dev_err_probe(dev, ret, "unable to get PRU1");
380*511f6c1aSRoger Quadros 			goto put_pru;
381*511f6c1aSRoger Quadros 		}
382*511f6c1aSRoger Quadros 	}
383*511f6c1aSRoger Quadros 
384*511f6c1aSRoger Quadros 	/* setup netdev interfaces */
385*511f6c1aSRoger Quadros 	if (eth0_node) {
386*511f6c1aSRoger Quadros 		ret = icssm_prueth_netdev_init(prueth, eth0_node);
387*511f6c1aSRoger Quadros 		if (ret) {
388*511f6c1aSRoger Quadros 			if (ret != -EPROBE_DEFER) {
389*511f6c1aSRoger Quadros 				dev_err(dev, "netdev init %s failed: %d\n",
390*511f6c1aSRoger Quadros 					eth0_node->name, ret);
391*511f6c1aSRoger Quadros 			}
392*511f6c1aSRoger Quadros 			goto put_pru;
393*511f6c1aSRoger Quadros 		}
394*511f6c1aSRoger Quadros 	}
395*511f6c1aSRoger Quadros 
396*511f6c1aSRoger Quadros 	if (eth1_node) {
397*511f6c1aSRoger Quadros 		ret = icssm_prueth_netdev_init(prueth, eth1_node);
398*511f6c1aSRoger Quadros 		if (ret) {
399*511f6c1aSRoger Quadros 			if (ret != -EPROBE_DEFER) {
400*511f6c1aSRoger Quadros 				dev_err(dev, "netdev init %s failed: %d\n",
401*511f6c1aSRoger Quadros 					eth1_node->name, ret);
402*511f6c1aSRoger Quadros 			}
403*511f6c1aSRoger Quadros 			goto netdev_exit;
404*511f6c1aSRoger Quadros 		}
405*511f6c1aSRoger Quadros 	}
406*511f6c1aSRoger Quadros 
407*511f6c1aSRoger Quadros 	/* register the network devices */
408*511f6c1aSRoger Quadros 	if (eth0_node) {
409*511f6c1aSRoger Quadros 		ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev);
410*511f6c1aSRoger Quadros 		if (ret) {
411*511f6c1aSRoger Quadros 			dev_err(dev, "can't register netdev for port MII0");
412*511f6c1aSRoger Quadros 			goto netdev_exit;
413*511f6c1aSRoger Quadros 		}
414*511f6c1aSRoger Quadros 
415*511f6c1aSRoger Quadros 		prueth->registered_netdevs[PRUETH_MAC0] =
416*511f6c1aSRoger Quadros 			prueth->emac[PRUETH_MAC0]->ndev;
417*511f6c1aSRoger Quadros 	}
418*511f6c1aSRoger Quadros 
419*511f6c1aSRoger Quadros 	if (eth1_node) {
420*511f6c1aSRoger Quadros 		ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev);
421*511f6c1aSRoger Quadros 		if (ret) {
422*511f6c1aSRoger Quadros 			dev_err(dev, "can't register netdev for port MII1");
423*511f6c1aSRoger Quadros 			goto netdev_unregister;
424*511f6c1aSRoger Quadros 		}
425*511f6c1aSRoger Quadros 
426*511f6c1aSRoger Quadros 		prueth->registered_netdevs[PRUETH_MAC1] =
427*511f6c1aSRoger Quadros 			prueth->emac[PRUETH_MAC1]->ndev;
428*511f6c1aSRoger Quadros 	}
429*511f6c1aSRoger Quadros 
430*511f6c1aSRoger Quadros 	if (eth1_node)
431*511f6c1aSRoger Quadros 		of_node_put(eth1_node);
432*511f6c1aSRoger Quadros 	if (eth0_node)
433*511f6c1aSRoger Quadros 		of_node_put(eth0_node);
434*511f6c1aSRoger Quadros 	return 0;
435*511f6c1aSRoger Quadros 
436*511f6c1aSRoger Quadros netdev_unregister:
437*511f6c1aSRoger Quadros 	for (i = 0; i < PRUETH_NUM_MACS; i++) {
438*511f6c1aSRoger Quadros 		if (!prueth->registered_netdevs[i])
439*511f6c1aSRoger Quadros 			continue;
440*511f6c1aSRoger Quadros 		unregister_netdev(prueth->registered_netdevs[i]);
441*511f6c1aSRoger Quadros 	}
442*511f6c1aSRoger Quadros 
443*511f6c1aSRoger Quadros netdev_exit:
444*511f6c1aSRoger Quadros 	for (i = 0; i < PRUETH_NUM_MACS; i++) {
445*511f6c1aSRoger Quadros 		eth_node = prueth->eth_node[i];
446*511f6c1aSRoger Quadros 		if (!eth_node)
447*511f6c1aSRoger Quadros 			continue;
448*511f6c1aSRoger Quadros 
449*511f6c1aSRoger Quadros 		icssm_prueth_netdev_exit(prueth, eth_node);
450*511f6c1aSRoger Quadros 	}
451*511f6c1aSRoger Quadros 
452*511f6c1aSRoger Quadros put_pru:
453*511f6c1aSRoger Quadros 	if (eth1_node) {
454*511f6c1aSRoger Quadros 		if (prueth->pru1)
455*511f6c1aSRoger Quadros 			pru_rproc_put(prueth->pru1);
456*511f6c1aSRoger Quadros 		of_node_put(eth1_node);
457*511f6c1aSRoger Quadros 	}
458*511f6c1aSRoger Quadros 
459*511f6c1aSRoger Quadros 	if (eth0_node) {
460*511f6c1aSRoger Quadros 		if (prueth->pru0)
461*511f6c1aSRoger Quadros 			pru_rproc_put(prueth->pru0);
462*511f6c1aSRoger Quadros 		of_node_put(eth0_node);
463*511f6c1aSRoger Quadros 	}
464*511f6c1aSRoger Quadros 
465*511f6c1aSRoger Quadros 	return ret;
466*511f6c1aSRoger Quadros }
467*511f6c1aSRoger Quadros 
468*511f6c1aSRoger Quadros static void icssm_prueth_remove(struct platform_device *pdev)
469*511f6c1aSRoger Quadros {
470*511f6c1aSRoger Quadros 	struct prueth *prueth = platform_get_drvdata(pdev);
471*511f6c1aSRoger Quadros 	struct device_node *eth_node;
472*511f6c1aSRoger Quadros 	int i;
473*511f6c1aSRoger Quadros 
474*511f6c1aSRoger Quadros 	for (i = 0; i < PRUETH_NUM_MACS; i++) {
475*511f6c1aSRoger Quadros 		if (!prueth->registered_netdevs[i])
476*511f6c1aSRoger Quadros 			continue;
477*511f6c1aSRoger Quadros 		unregister_netdev(prueth->registered_netdevs[i]);
478*511f6c1aSRoger Quadros 	}
479*511f6c1aSRoger Quadros 
480*511f6c1aSRoger Quadros 	for (i = 0; i < PRUETH_NUM_MACS; i++) {
481*511f6c1aSRoger Quadros 		eth_node = prueth->eth_node[i];
482*511f6c1aSRoger Quadros 		if (!eth_node)
483*511f6c1aSRoger Quadros 			continue;
484*511f6c1aSRoger Quadros 
485*511f6c1aSRoger Quadros 		icssm_prueth_netdev_exit(prueth, eth_node);
486*511f6c1aSRoger Quadros 		of_node_put(eth_node);
487*511f6c1aSRoger Quadros 	}
488*511f6c1aSRoger Quadros 
489*511f6c1aSRoger Quadros 	pruss_put(prueth->pruss);
490*511f6c1aSRoger Quadros 
491*511f6c1aSRoger Quadros 	if (prueth->eth_node[PRUETH_MAC0])
492*511f6c1aSRoger Quadros 		pru_rproc_put(prueth->pru0);
493*511f6c1aSRoger Quadros 	if (prueth->eth_node[PRUETH_MAC1])
494*511f6c1aSRoger Quadros 		pru_rproc_put(prueth->pru1);
495*511f6c1aSRoger Quadros }
496*511f6c1aSRoger Quadros 
497*511f6c1aSRoger Quadros #ifdef CONFIG_PM_SLEEP
498*511f6c1aSRoger Quadros static int icssm_prueth_suspend(struct device *dev)
499*511f6c1aSRoger Quadros {
500*511f6c1aSRoger Quadros 	struct prueth *prueth = dev_get_drvdata(dev);
501*511f6c1aSRoger Quadros 	struct net_device *ndev;
502*511f6c1aSRoger Quadros 	int i, ret;
503*511f6c1aSRoger Quadros 
504*511f6c1aSRoger Quadros 	for (i = 0; i < PRUETH_NUM_MACS; i++) {
505*511f6c1aSRoger Quadros 		ndev = prueth->registered_netdevs[i];
506*511f6c1aSRoger Quadros 
507*511f6c1aSRoger Quadros 		if (!ndev)
508*511f6c1aSRoger Quadros 			continue;
509*511f6c1aSRoger Quadros 
510*511f6c1aSRoger Quadros 		if (netif_running(ndev)) {
511*511f6c1aSRoger Quadros 			netif_device_detach(ndev);
512*511f6c1aSRoger Quadros 			ret = icssm_emac_ndo_stop(ndev);
513*511f6c1aSRoger Quadros 			if (ret < 0) {
514*511f6c1aSRoger Quadros 				netdev_err(ndev, "failed to stop: %d", ret);
515*511f6c1aSRoger Quadros 				return ret;
516*511f6c1aSRoger Quadros 			}
517*511f6c1aSRoger Quadros 		}
518*511f6c1aSRoger Quadros 	}
519*511f6c1aSRoger Quadros 
520*511f6c1aSRoger Quadros 	return 0;
521*511f6c1aSRoger Quadros }
522*511f6c1aSRoger Quadros 
523*511f6c1aSRoger Quadros static int icssm_prueth_resume(struct device *dev)
524*511f6c1aSRoger Quadros {
525*511f6c1aSRoger Quadros 	struct prueth *prueth = dev_get_drvdata(dev);
526*511f6c1aSRoger Quadros 	struct net_device *ndev;
527*511f6c1aSRoger Quadros 	int i, ret;
528*511f6c1aSRoger Quadros 
529*511f6c1aSRoger Quadros 	for (i = 0; i < PRUETH_NUM_MACS; i++) {
530*511f6c1aSRoger Quadros 		ndev = prueth->registered_netdevs[i];
531*511f6c1aSRoger Quadros 
532*511f6c1aSRoger Quadros 		if (!ndev)
533*511f6c1aSRoger Quadros 			continue;
534*511f6c1aSRoger Quadros 
535*511f6c1aSRoger Quadros 		if (netif_running(ndev)) {
536*511f6c1aSRoger Quadros 			ret = icssm_emac_ndo_open(ndev);
537*511f6c1aSRoger Quadros 			if (ret < 0) {
538*511f6c1aSRoger Quadros 				netdev_err(ndev, "failed to start: %d", ret);
539*511f6c1aSRoger Quadros 				return ret;
540*511f6c1aSRoger Quadros 			}
541*511f6c1aSRoger Quadros 			netif_device_attach(ndev);
542*511f6c1aSRoger Quadros 		}
543*511f6c1aSRoger Quadros 	}
544*511f6c1aSRoger Quadros 
545*511f6c1aSRoger Quadros 	return 0;
546*511f6c1aSRoger Quadros }
547*511f6c1aSRoger Quadros 
548*511f6c1aSRoger Quadros #endif /* CONFIG_PM_SLEEP */
549*511f6c1aSRoger Quadros 
550*511f6c1aSRoger Quadros static const struct dev_pm_ops prueth_dev_pm_ops = {
551*511f6c1aSRoger Quadros 	SET_SYSTEM_SLEEP_PM_OPS(icssm_prueth_suspend, icssm_prueth_resume)
552*511f6c1aSRoger Quadros };
553*511f6c1aSRoger Quadros 
554*511f6c1aSRoger Quadros /* AM335x SoC-specific firmware data */
555*511f6c1aSRoger Quadros static struct prueth_private_data am335x_prueth_pdata = {
556*511f6c1aSRoger Quadros 	.fw_pru[PRUSS_PRU0] = {
557*511f6c1aSRoger Quadros 		.fw_name[PRUSS_ETHTYPE_EMAC] =
558*511f6c1aSRoger Quadros 			"ti-pruss/am335x-pru0-prueth-fw.elf",
559*511f6c1aSRoger Quadros 	},
560*511f6c1aSRoger Quadros 	.fw_pru[PRUSS_PRU1] = {
561*511f6c1aSRoger Quadros 		.fw_name[PRUSS_ETHTYPE_EMAC] =
562*511f6c1aSRoger Quadros 			"ti-pruss/am335x-pru1-prueth-fw.elf",
563*511f6c1aSRoger Quadros 	},
564*511f6c1aSRoger Quadros };
565*511f6c1aSRoger Quadros 
566*511f6c1aSRoger Quadros /* AM437x SoC-specific firmware data */
567*511f6c1aSRoger Quadros static struct prueth_private_data am437x_prueth_pdata = {
568*511f6c1aSRoger Quadros 	.fw_pru[PRUSS_PRU0] = {
569*511f6c1aSRoger Quadros 		.fw_name[PRUSS_ETHTYPE_EMAC] =
570*511f6c1aSRoger Quadros 			"ti-pruss/am437x-pru0-prueth-fw.elf",
571*511f6c1aSRoger Quadros 	},
572*511f6c1aSRoger Quadros 	.fw_pru[PRUSS_PRU1] = {
573*511f6c1aSRoger Quadros 		.fw_name[PRUSS_ETHTYPE_EMAC] =
574*511f6c1aSRoger Quadros 			"ti-pruss/am437x-pru1-prueth-fw.elf",
575*511f6c1aSRoger Quadros 	},
576*511f6c1aSRoger Quadros };
577*511f6c1aSRoger Quadros 
578*511f6c1aSRoger Quadros /* AM57xx SoC-specific firmware data */
579*511f6c1aSRoger Quadros static struct prueth_private_data am57xx_prueth_pdata = {
580*511f6c1aSRoger Quadros 	.fw_pru[PRUSS_PRU0] = {
581*511f6c1aSRoger Quadros 		.fw_name[PRUSS_ETHTYPE_EMAC] =
582*511f6c1aSRoger Quadros 			"ti-pruss/am57xx-pru0-prueth-fw.elf",
583*511f6c1aSRoger Quadros 	},
584*511f6c1aSRoger Quadros 	.fw_pru[PRUSS_PRU1] = {
585*511f6c1aSRoger Quadros 		.fw_name[PRUSS_ETHTYPE_EMAC] =
586*511f6c1aSRoger Quadros 			"ti-pruss/am57xx-pru1-prueth-fw.elf",
587*511f6c1aSRoger Quadros 	},
588*511f6c1aSRoger Quadros };
589*511f6c1aSRoger Quadros 
590*511f6c1aSRoger Quadros static const struct of_device_id prueth_dt_match[] = {
591*511f6c1aSRoger Quadros 	{ .compatible = "ti,am57-prueth", .data = &am57xx_prueth_pdata, },
592*511f6c1aSRoger Quadros 	{ .compatible = "ti,am4376-prueth", .data = &am437x_prueth_pdata, },
593*511f6c1aSRoger Quadros 	{ .compatible = "ti,am3359-prueth", .data = &am335x_prueth_pdata, },
594*511f6c1aSRoger Quadros 	{ /* sentinel */ }
595*511f6c1aSRoger Quadros };
596*511f6c1aSRoger Quadros MODULE_DEVICE_TABLE(of, prueth_dt_match);
597*511f6c1aSRoger Quadros 
598*511f6c1aSRoger Quadros static struct platform_driver prueth_driver = {
599*511f6c1aSRoger Quadros 	.probe = icssm_prueth_probe,
600*511f6c1aSRoger Quadros 	.remove = icssm_prueth_remove,
601*511f6c1aSRoger Quadros 	.driver = {
602*511f6c1aSRoger Quadros 		.name = "prueth",
603*511f6c1aSRoger Quadros 		.of_match_table = prueth_dt_match,
604*511f6c1aSRoger Quadros 		.pm = &prueth_dev_pm_ops,
605*511f6c1aSRoger Quadros 	},
606*511f6c1aSRoger Quadros };
607*511f6c1aSRoger Quadros module_platform_driver(prueth_driver);
608*511f6c1aSRoger Quadros 
609*511f6c1aSRoger Quadros MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
610*511f6c1aSRoger Quadros MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
611*511f6c1aSRoger Quadros MODULE_DESCRIPTION("PRUSS ICSSM Ethernet Driver");
612*511f6c1aSRoger Quadros MODULE_LICENSE("GPL");
613