summaryrefslogtreecommitdiffstats
path: root/board/freescale/common/sgmii_riser.c
blob: 23157930101f401f332b3a9e94948a34899de62b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
 * Freescale SGMII Riser Card
 *
 * This driver supports the SGMII Riser card found on the
 * "DS" style of development board from Freescale.
 *
 * This software may be used and distributed according to the
 * terms of the GNU Public License, Version 2, incorporated
 * herein by reference.
 *
 * Copyright 2008 Freescale Semiconductor, Inc.
 *
 */

#include <config.h>
#include <common.h>
#include <log.h>
#include <net.h>
#include <linux/libfdt.h>
#include <tsec.h>
#include <fdt_support.h>

void fsl_sgmii_riser_init(struct tsec_info_struct *tsec_info, int num)
{
	int i;

	for (i = 0; i < num; i++)
		if (tsec_info[i].flags & TSEC_SGMII)
			tsec_info[i].phyaddr += SGMII_RISER_PHY_OFFSET;
}

void fsl_sgmii_riser_fdt_fixup(void *fdt)
{
	struct eth_device *dev;
	int node;
	int mdio_node;
	int i = -1;
	int etsec_num = 0;

	node = fdt_path_offset(fdt, "/aliases");
	if (node < 0)
		return;

	while ((dev = eth_get_dev_by_index(++i)) != NULL) {
		struct tsec_private *priv;
		int phy_node;
		int enet_node;
		uint32_t ph;
		char sgmii_phy[16];
		char enet[16];
		const u32 *phyh;
		const char *model;
		const char *path;

		if (!strstr(dev->name, "eTSEC"))
			continue;

		priv = dev->priv;
		if (!(priv->flags & TSEC_SGMII)) {
			etsec_num++;
			continue;
		}

		mdio_node = fdt_node_offset_by_compatible(fdt, -1,
				"fsl,gianfar-mdio");
		if (mdio_node < 0)
			return;

		sprintf(sgmii_phy, "sgmii-phy@%d", etsec_num);
		phy_node = fdt_subnode_offset(fdt, mdio_node, sgmii_phy);
		if (phy_node > 0) {
			fdt_increase_size(fdt, 32);
			ph = fdt_create_phandle(fdt, phy_node);
			if (!ph)
				continue;
		}

		sprintf(enet, "ethernet%d", etsec_num++);
		path = fdt_getprop(fdt, node, enet, NULL);
		if (!path) {
			debug("No alias for %s\n", enet);
			continue;
		}

		enet_node = fdt_path_offset(fdt, path);
		if (enet_node < 0)
			continue;

		model = fdt_getprop(fdt, enet_node, "model", NULL);

		/*
		 * We only want to do this to eTSECs.  On some platforms
		 * there are more than one type of gianfar-style ethernet
		 * controller, and as we are creating an implicit connection
		 * between ethernet nodes and eTSEC devices, it is best to
		 * make the connection use as much explicit information
		 * as exists.
		 */
		if (!strstr(model, "TSEC"))
			continue;

		if (phy_node < 0) {
			/*
			 * This part is only for old device tree without
			 * sgmii_phy nodes. It's kept just for compatible
			 * reason. Soon to be deprecated if all device tree
			 * get updated.
			 */
			phyh = fdt_getprop(fdt, enet_node, "phy-handle", NULL);
			if (!phyh)
				continue;

			phy_node = fdt_node_offset_by_phandle(fdt,
					fdt32_to_cpu(*phyh));

			priv = dev->priv;

			if (priv->flags & TSEC_SGMII)
				fdt_setprop_cell(fdt, phy_node, "reg",
						priv->phyaddr);
		} else {
			fdt_setprop(fdt, enet_node, "phy-handle", &ph,
					sizeof(ph));
			fdt_setprop_string(fdt, enet_node,
					"phy-connection-type",
					phy_string_for_interface(
						PHY_INTERFACE_MODE_SGMII));
		}
	}
}