1 /*
2  * Freescale SGMII Riser Card
3  *
4  * This driver supports the SGMII Riser card found on the
5  * "DS" style of development board from Freescale.
6  *
7  * This software may be used and distributed according to the
8  * terms of the GNU Public License, Version 2, incorporated
9  * herein by reference.
10  *
11  * Copyright 2008 Freescale Semiconductor, Inc.
12  *
13  */
14 
15 #include <config.h>
16 #include <common.h>
17 #include <log.h>
18 #include <net.h>
19 #include <linux/libfdt.h>
20 #include <tsec.h>
21 #include <fdt_support.h>
22 
fsl_sgmii_riser_init(struct tsec_info_struct * tsec_info,int num)23 void fsl_sgmii_riser_init(struct tsec_info_struct *tsec_info, int num)
24 {
25 	int i;
26 
27 	for (i = 0; i < num; i++)
28 		if (tsec_info[i].flags & TSEC_SGMII)
29 			tsec_info[i].phyaddr += SGMII_RISER_PHY_OFFSET;
30 }
31 
fsl_sgmii_riser_fdt_fixup(void * fdt)32 void fsl_sgmii_riser_fdt_fixup(void *fdt)
33 {
34 	struct eth_device *dev;
35 	int node;
36 	int mdio_node;
37 	int i = -1;
38 	int etsec_num = 0;
39 
40 	node = fdt_path_offset(fdt, "/aliases");
41 	if (node < 0)
42 		return;
43 
44 	while ((dev = eth_get_dev_by_index(++i)) != NULL) {
45 		struct tsec_private *priv;
46 		int phy_node;
47 		int enet_node;
48 		uint32_t ph;
49 		char sgmii_phy[16];
50 		char enet[16];
51 		const u32 *phyh;
52 		const char *model;
53 		const char *path;
54 
55 		if (!strstr(dev->name, "eTSEC"))
56 			continue;
57 
58 		priv = dev->priv;
59 		if (!(priv->flags & TSEC_SGMII)) {
60 			etsec_num++;
61 			continue;
62 		}
63 
64 		mdio_node = fdt_node_offset_by_compatible(fdt, -1,
65 				"fsl,gianfar-mdio");
66 		if (mdio_node < 0)
67 			return;
68 
69 		sprintf(sgmii_phy, "sgmii-phy@%d", etsec_num);
70 		phy_node = fdt_subnode_offset(fdt, mdio_node, sgmii_phy);
71 		if (phy_node > 0) {
72 			fdt_increase_size(fdt, 32);
73 			ph = fdt_create_phandle(fdt, phy_node);
74 			if (!ph)
75 				continue;
76 		}
77 
78 		sprintf(enet, "ethernet%d", etsec_num++);
79 		path = fdt_getprop(fdt, node, enet, NULL);
80 		if (!path) {
81 			debug("No alias for %s\n", enet);
82 			continue;
83 		}
84 
85 		enet_node = fdt_path_offset(fdt, path);
86 		if (enet_node < 0)
87 			continue;
88 
89 		model = fdt_getprop(fdt, enet_node, "model", NULL);
90 
91 		/*
92 		 * We only want to do this to eTSECs.  On some platforms
93 		 * there are more than one type of gianfar-style ethernet
94 		 * controller, and as we are creating an implicit connection
95 		 * between ethernet nodes and eTSEC devices, it is best to
96 		 * make the connection use as much explicit information
97 		 * as exists.
98 		 */
99 		if (!strstr(model, "TSEC"))
100 			continue;
101 
102 		if (phy_node < 0) {
103 			/*
104 			 * This part is only for old device tree without
105 			 * sgmii_phy nodes. It's kept just for compatible
106 			 * reason. Soon to be deprecated if all device tree
107 			 * get updated.
108 			 */
109 			phyh = fdt_getprop(fdt, enet_node, "phy-handle", NULL);
110 			if (!phyh)
111 				continue;
112 
113 			phy_node = fdt_node_offset_by_phandle(fdt,
114 					fdt32_to_cpu(*phyh));
115 
116 			priv = dev->priv;
117 
118 			if (priv->flags & TSEC_SGMII)
119 				fdt_setprop_cell(fdt, phy_node, "reg",
120 						priv->phyaddr);
121 		} else {
122 			fdt_setprop(fdt, enet_node, "phy-handle", &ph,
123 					sizeof(ph));
124 			fdt_setprop_string(fdt, enet_node,
125 					"phy-connection-type",
126 					phy_string_for_interface(
127 						PHY_INTERFACE_MODE_SGMII));
128 		}
129 	}
130 }
131