summaryrefslogtreecommitdiffstats
path: root/board/freescale/ls1021aqds/eth.c
blob: bf3e08a8cf9dcad6bddcfb072183223c6c7ec682 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
 * Copyright 2014 Freescale Semiconductor, Inc.
 *
 * SPDX-License-Identifier:	GPL-2.0+
 *
 * This file handles the board muxing between the RGMII/SGMII PHYs on
 * Freescale LS1021AQDS board. The RGMII PHYs are the three on-board 1Gb
 * ports. The SGMII PHYs are provided by the standard Freescale four-port
 * SGMII riser card.
 *
 * Muxing is handled via the PIXIS BRDCFG4 register. The EMI1 bits control
 * muxing among the RGMII PHYs and the SGMII PHYs. The value for RGMII depends
 * on which port is used. The value for SGMII depends on which slot the riser
 * is inserted in.
 */

#include <common.h>
#include <netdev.h>
#include <asm/arch/fsl_serdes.h>
#include <fsl_mdio.h>
#include <tsec.h>
#include <malloc.h>

#include "../common/sgmii_riser.h"
#include "../common/qixis.h"

#define EMI1_MASK       0x1f
#define EMI1_RGMII0     1
#define EMI1_RGMII1     2
#define EMI1_RGMII2     3
#define EMI1_SGMII1     0x1c
#define EMI1_SGMII2     0x1d

struct ls1021a_mdio {
	struct mii_dev *realbus;
};

static void ls1021a_mux_mdio(int addr)
{
	u8 brdcfg4;

	brdcfg4 = QIXIS_READ(brdcfg[4]);
	brdcfg4 &= EMI1_MASK;

	switch (addr) {
	case EMI1_RGMII0:
		brdcfg4 |= 0;
		break;
	case EMI1_RGMII1:
		brdcfg4 |= 0x20;
		break;
	case EMI1_RGMII2:
		brdcfg4 |= 0x40;
		break;
	case EMI1_SGMII1:
		brdcfg4 |= 0x60;
		break;
	case EMI1_SGMII2:
		brdcfg4 |= 0x80;
		break;
	default:
		brdcfg4 |= 0xa0;
		break;
	}

	QIXIS_WRITE(brdcfg[4], brdcfg4);
}

static int ls1021a_mdio_read(struct mii_dev *bus, int addr, int devad,
			     int regnum)
{
	struct ls1021a_mdio *priv = bus->priv;

	ls1021a_mux_mdio(addr);

	return priv->realbus->read(priv->realbus, addr, devad, regnum);
}

static int ls1021a_mdio_write(struct mii_dev *bus, int addr, int devad,
			      int regnum, u16 value)
{
	struct ls1021a_mdio *priv = bus->priv;

	ls1021a_mux_mdio(addr);

	return priv->realbus->write(priv->realbus, addr, devad, regnum, value);
}

static int ls1021a_mdio_reset(struct mii_dev *bus)
{
	struct ls1021a_mdio *priv = bus->priv;

	return priv->realbus->reset(priv->realbus);
}

static int ls1021a_mdio_init(char *realbusname, char *fakebusname)
{
	struct ls1021a_mdio *lsmdio;
	struct mii_dev *bus = mdio_alloc();

	if (!bus) {
		printf("Failed to allocate LS102xA MDIO bus\n");
		return -1;
	}

	lsmdio = malloc(sizeof(*lsmdio));
	if (!lsmdio) {
		printf("Failed to allocate LS102xA private data\n");
		free(bus);
		return -1;
	}

	bus->read = ls1021a_mdio_read;
	bus->write = ls1021a_mdio_write;
	bus->reset = ls1021a_mdio_reset;
	strcpy(bus->name, fakebusname);

	lsmdio->realbus = miiphy_get_dev_by_name(realbusname);

	if (!lsmdio->realbus) {
		printf("No bus with name %s\n", realbusname);
		free(bus);
		free(lsmdio);
		return -1;
	}

	bus->priv = lsmdio;

	return mdio_register(bus);
}

int board_eth_init(bd_t *bis)
{
	struct fsl_pq_mdio_info mdio_info;
	struct tsec_info_struct tsec_info[3];
	int num = 0;

#ifdef CONFIG_TSEC1
	SET_STD_TSEC_INFO(tsec_info[num], 1);
	if (is_serdes_configured(SGMII_TSEC1)) {
		puts("eTSEC1 is in sgmii mode\n");
		tsec_info[num].flags |= TSEC_SGMII;
		tsec_info[num].mii_devname = "LS1021A_SGMII_MDIO";
	} else {
		tsec_info[num].mii_devname = "LS1021A_RGMII_MDIO";
	}
	num++;
#endif
#ifdef CONFIG_TSEC2
	SET_STD_TSEC_INFO(tsec_info[num], 2);
	if (is_serdes_configured(SGMII_TSEC2)) {
		puts("eTSEC2 is in sgmii mode\n");
		tsec_info[num].flags |= TSEC_SGMII;
		tsec_info[num].mii_devname = "LS1021A_SGMII_MDIO";
	} else {
		tsec_info[num].mii_devname = "LS1021A_RGMII_MDIO";
	}
	num++;
#endif
#ifdef CONFIG_TSEC3
	SET_STD_TSEC_INFO(tsec_info[num], 3);
	tsec_info[num].mii_devname = "LS1021A_RGMII_MDIO";
	num++;
#endif
	if (!num) {
		printf("No TSECs initialized\n");
		return 0;
	}

#ifdef CONFIG_FSL_SGMII_RISER
	fsl_sgmii_riser_init(tsec_info, num);
#endif

	mdio_info.regs = (struct tsec_mii_mng *)CONFIG_SYS_MDIO_BASE_ADDR;
	mdio_info.name = DEFAULT_MII_NAME;

	fsl_pq_mdio_init(bis, &mdio_info);

	/* Register the virtual MDIO front-ends */
	ls1021a_mdio_init(DEFAULT_MII_NAME, "LS1021A_RGMII_MDIO");
	ls1021a_mdio_init(DEFAULT_MII_NAME, "LS1021A_SGMII_MDIO");

	tsec_eth_init(bis, tsec_info, num);

	return pci_eth_init(bis);
}
OpenPOWER on IntegriCloud