xref: /rk3399_rockchip-uboot/drivers/net/smc911x.c (revision 03f3d8d3b39cf85c0ce7ca903b436701e8aa610b)
1de1b686bSSascha Hauer /*
2de1b686bSSascha Hauer  * SMSC LAN9[12]1[567] Network driver
3de1b686bSSascha Hauer  *
4cce9cfdaSStelian Pop  * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
5de1b686bSSascha Hauer  *
6de1b686bSSascha Hauer  * See file CREDITS for list of people who contributed to this
7de1b686bSSascha Hauer  * project.
8de1b686bSSascha Hauer  *
9de1b686bSSascha Hauer  * This program is free software; you can redistribute it and/or
10de1b686bSSascha Hauer  * modify it under the terms of the GNU General Public License as
11de1b686bSSascha Hauer  * published by the Free Software Foundation; either version 2 of
12de1b686bSSascha Hauer  * the License, or (at your option) any later version.
13de1b686bSSascha Hauer  *
14de1b686bSSascha Hauer  * This program is distributed in the hope that it will be useful,
15de1b686bSSascha Hauer  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16de1b686bSSascha Hauer  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17de1b686bSSascha Hauer  * GNU General Public License for more details.
18de1b686bSSascha Hauer  *
19de1b686bSSascha Hauer  * You should have received a copy of the GNU General Public License
20de1b686bSSascha Hauer  * along with this program; if not, write to the Free Software
21de1b686bSSascha Hauer  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22de1b686bSSascha Hauer  * MA 02111-1307 USA
23de1b686bSSascha Hauer  */
24de1b686bSSascha Hauer 
25de1b686bSSascha Hauer #include <common.h>
26de1b686bSSascha Hauer #include <command.h>
27de1b686bSSascha Hauer #include <net.h>
28de1b686bSSascha Hauer #include <miiphy.h>
29de1b686bSSascha Hauer 
3075ba6d69SMike Frysinger #include "smc911x.h"
31de1b686bSSascha Hauer 
3233314470SNobuhiro Iwamatsu u32 pkt_data_pull(u32 addr) \
33890a02e8SStefan Roese 	__attribute__ ((weak, alias ("smc911x_reg_read")));
3433314470SNobuhiro Iwamatsu void pkt_data_push(u32 addr, u32 val) \
35890a02e8SStefan Roese 	__attribute__ ((weak, alias ("smc911x_reg_write")));
3633314470SNobuhiro Iwamatsu 
373e0f331cSGuennadi Liakhovetski #define mdelay(n)       udelay((n)*1000)
38de1b686bSSascha Hauer 
39de1b686bSSascha Hauer static int smx911x_handle_mac_address(bd_t *bd)
40de1b686bSSascha Hauer {
41de1b686bSSascha Hauer 	unsigned long addrh, addrl;
42*03f3d8d3SMike Frysinger 	uchar m[6];
43de1b686bSSascha Hauer 
44de1b686bSSascha Hauer 	/* if the environment has a valid mac address then use it */
45*03f3d8d3SMike Frysinger 	if (!eth_getenv_enetaddr("ethaddr", m)) {
46de1b686bSSascha Hauer 		/* if not, try to get one from the eeprom */
47de1b686bSSascha Hauer 		addrh = smc911x_get_mac_csr(ADDRH);
48de1b686bSSascha Hauer 		addrl = smc911x_get_mac_csr(ADDRL);
49de1b686bSSascha Hauer 
50de1b686bSSascha Hauer 		m[0] = (addrl       ) & 0xff;
51de1b686bSSascha Hauer 		m[1] = (addrl >>  8 ) & 0xff;
52de1b686bSSascha Hauer 		m[2] = (addrl >> 16 ) & 0xff;
53de1b686bSSascha Hauer 		m[3] = (addrl >> 24 ) & 0xff;
54de1b686bSSascha Hauer 		m[4] = (addrh       ) & 0xff;
55de1b686bSSascha Hauer 		m[5] = (addrh >>  8 ) & 0xff;
56de1b686bSSascha Hauer 
57de1b686bSSascha Hauer 		/* we get 0xff when there is no eeprom connected */
58de1b686bSSascha Hauer 		if ((m[0] & m[1] & m[2] & m[3] & m[4] & m[5]) == 0xff) {
59de1b686bSSascha Hauer 			printf(DRIVERNAME ": no valid mac address in environment "
60de1b686bSSascha Hauer 				"and no eeprom found\n");
61de1b686bSSascha Hauer 			return -1;
62de1b686bSSascha Hauer 		}
63*03f3d8d3SMike Frysinger 
64*03f3d8d3SMike Frysinger 		eth_setenv_enetaddr("ethaddr", m);
65de1b686bSSascha Hauer 	}
66de1b686bSSascha Hauer 
67*03f3d8d3SMike Frysinger 	printf(DRIVERNAME ": MAC %pM\n", m);
68de1b686bSSascha Hauer 
69de1b686bSSascha Hauer 	return 0;
70de1b686bSSascha Hauer }
71de1b686bSSascha Hauer 
72de1b686bSSascha Hauer static int smc911x_miiphy_read(u8 phy, u8 reg, u16 *val)
73de1b686bSSascha Hauer {
743e0f331cSGuennadi Liakhovetski 	while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
753e0f331cSGuennadi Liakhovetski 		;
76de1b686bSSascha Hauer 
77de1b686bSSascha Hauer 	smc911x_set_mac_csr(MII_ACC, phy << 11 | reg << 6 | MII_ACC_MII_BUSY);
78de1b686bSSascha Hauer 
793e0f331cSGuennadi Liakhovetski 	while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
803e0f331cSGuennadi Liakhovetski 		;
81de1b686bSSascha Hauer 
82de1b686bSSascha Hauer 	*val = smc911x_get_mac_csr(MII_DATA);
83de1b686bSSascha Hauer 
84de1b686bSSascha Hauer 	return 0;
85de1b686bSSascha Hauer }
86de1b686bSSascha Hauer 
87de1b686bSSascha Hauer static int smc911x_miiphy_write(u8 phy, u8 reg, u16  val)
88de1b686bSSascha Hauer {
893e0f331cSGuennadi Liakhovetski 	while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
903e0f331cSGuennadi Liakhovetski 		;
91de1b686bSSascha Hauer 
92de1b686bSSascha Hauer 	smc911x_set_mac_csr(MII_DATA, val);
93de1b686bSSascha Hauer 	smc911x_set_mac_csr(MII_ACC,
94de1b686bSSascha Hauer 		phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE);
95de1b686bSSascha Hauer 
963e0f331cSGuennadi Liakhovetski 	while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
973e0f331cSGuennadi Liakhovetski 		;
98de1b686bSSascha Hauer 	return 0;
99de1b686bSSascha Hauer }
100de1b686bSSascha Hauer 
101de1b686bSSascha Hauer static int smc911x_phy_reset(void)
102de1b686bSSascha Hauer {
103de1b686bSSascha Hauer 	u32 reg;
104de1b686bSSascha Hauer 
105890a02e8SStefan Roese 	reg = smc911x_reg_read(PMT_CTRL);
106de1b686bSSascha Hauer 	reg &= ~0xfffff030;
107de1b686bSSascha Hauer 	reg |= PMT_CTRL_PHY_RST;
108890a02e8SStefan Roese 	smc911x_reg_write(PMT_CTRL, reg);
109de1b686bSSascha Hauer 
110de1b686bSSascha Hauer 	mdelay(100);
111de1b686bSSascha Hauer 
112de1b686bSSascha Hauer 	return 0;
113de1b686bSSascha Hauer }
114de1b686bSSascha Hauer 
115de1b686bSSascha Hauer static void smc911x_phy_configure(void)
116de1b686bSSascha Hauer {
117de1b686bSSascha Hauer 	int timeout;
118de1b686bSSascha Hauer 	u16 status;
119de1b686bSSascha Hauer 
120de1b686bSSascha Hauer 	smc911x_phy_reset();
121de1b686bSSascha Hauer 
122de1b686bSSascha Hauer 	smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_RESET);
123de1b686bSSascha Hauer 	mdelay(1);
124de1b686bSSascha Hauer 	smc911x_miiphy_write(1, PHY_ANAR, 0x01e1);
125de1b686bSSascha Hauer 	smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG);
126de1b686bSSascha Hauer 
127de1b686bSSascha Hauer 	timeout = 5000;
128de1b686bSSascha Hauer 	do {
129de1b686bSSascha Hauer 		mdelay(1);
130de1b686bSSascha Hauer 		if ((timeout--) == 0)
131de1b686bSSascha Hauer 			goto err_out;
132de1b686bSSascha Hauer 
133de1b686bSSascha Hauer 		if (smc911x_miiphy_read(1, PHY_BMSR, &status) != 0)
134de1b686bSSascha Hauer 			goto err_out;
135de1b686bSSascha Hauer 	} while (!(status & PHY_BMSR_LS));
136de1b686bSSascha Hauer 
137de1b686bSSascha Hauer 	printf(DRIVERNAME ": phy initialized\n");
138de1b686bSSascha Hauer 
139de1b686bSSascha Hauer 	return;
140de1b686bSSascha Hauer 
141de1b686bSSascha Hauer err_out:
142de1b686bSSascha Hauer 	printf(DRIVERNAME ": autonegotiation timed out\n");
143de1b686bSSascha Hauer }
144de1b686bSSascha Hauer 
145de1b686bSSascha Hauer static void smc911x_enable(void)
146de1b686bSSascha Hauer {
147de1b686bSSascha Hauer 	/* Enable TX */
148890a02e8SStefan Roese 	smc911x_reg_write(HW_CFG, 8 << 16 | HW_CFG_SF);
149de1b686bSSascha Hauer 
150890a02e8SStefan Roese 	smc911x_reg_write(GPT_CFG, GPT_CFG_TIMER_EN | 10000);
151de1b686bSSascha Hauer 
152890a02e8SStefan Roese 	smc911x_reg_write(TX_CFG, TX_CFG_TX_ON);
153de1b686bSSascha Hauer 
154de1b686bSSascha Hauer 	/* no padding to start of packets */
155890a02e8SStefan Roese 	smc911x_reg_write(RX_CFG, 0);
156de1b686bSSascha Hauer 
157de1b686bSSascha Hauer 	smc911x_set_mac_csr(MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | MAC_CR_HBDIS);
158de1b686bSSascha Hauer 
159de1b686bSSascha Hauer }
160de1b686bSSascha Hauer 
161de1b686bSSascha Hauer int eth_init(bd_t *bd)
162de1b686bSSascha Hauer {
163de1b686bSSascha Hauer 	printf(DRIVERNAME ": initializing\n");
164de1b686bSSascha Hauer 
16575ba6d69SMike Frysinger 	if (smc911x_detect_chip())
166de1b686bSSascha Hauer 		goto err_out;
167de1b686bSSascha Hauer 
168de1b686bSSascha Hauer 	smc911x_reset();
169de1b686bSSascha Hauer 
170de1b686bSSascha Hauer 	/* Configure the PHY, initialize the link state */
171de1b686bSSascha Hauer 	smc911x_phy_configure();
172de1b686bSSascha Hauer 
173de1b686bSSascha Hauer 	if (smx911x_handle_mac_address(bd))
174de1b686bSSascha Hauer 		goto err_out;
175de1b686bSSascha Hauer 
176de1b686bSSascha Hauer 	/* Turn on Tx + Rx */
177de1b686bSSascha Hauer 	smc911x_enable();
178de1b686bSSascha Hauer 
179de1b686bSSascha Hauer 	return 0;
180de1b686bSSascha Hauer 
181de1b686bSSascha Hauer err_out:
182de1b686bSSascha Hauer 	return -1;
183de1b686bSSascha Hauer }
184de1b686bSSascha Hauer 
185de1b686bSSascha Hauer int eth_send(volatile void *packet, int length)
186de1b686bSSascha Hauer {
187de1b686bSSascha Hauer 	u32 *data = (u32*)packet;
188de1b686bSSascha Hauer 	u32 tmplen;
189de1b686bSSascha Hauer 	u32 status;
190de1b686bSSascha Hauer 
191890a02e8SStefan Roese 	smc911x_reg_write(TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG | TX_CMD_A_INT_LAST_SEG | length);
192890a02e8SStefan Roese 	smc911x_reg_write(TX_DATA_FIFO, length);
193de1b686bSSascha Hauer 
194de1b686bSSascha Hauer 	tmplen = (length + 3) / 4;
195de1b686bSSascha Hauer 
196de1b686bSSascha Hauer 	while (tmplen--)
19733314470SNobuhiro Iwamatsu 		pkt_data_push(TX_DATA_FIFO, *data++);
198de1b686bSSascha Hauer 
199de1b686bSSascha Hauer 	/* wait for transmission */
200890a02e8SStefan Roese 	while (!((smc911x_reg_read(TX_FIFO_INF) & TX_FIFO_INF_TSUSED) >> 16));
201de1b686bSSascha Hauer 
202de1b686bSSascha Hauer 	/* get status. Ignore 'no carrier' error, it has no meaning for
203de1b686bSSascha Hauer 	 * full duplex operation
204de1b686bSSascha Hauer 	 */
205890a02e8SStefan Roese 	status = smc911x_reg_read(TX_STATUS_FIFO) & (TX_STS_LOC | TX_STS_LATE_COLL |
206de1b686bSSascha Hauer 		TX_STS_MANY_COLL | TX_STS_MANY_DEFER | TX_STS_UNDERRUN);
207de1b686bSSascha Hauer 
208de1b686bSSascha Hauer 	if (!status)
209de1b686bSSascha Hauer 		return 0;
210de1b686bSSascha Hauer 
211de1b686bSSascha Hauer 	printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n",
212de1b686bSSascha Hauer 		status & TX_STS_LOC ? "TX_STS_LOC " : "",
213de1b686bSSascha Hauer 		status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "",
214de1b686bSSascha Hauer 		status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "",
215de1b686bSSascha Hauer 		status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "",
216de1b686bSSascha Hauer 		status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : "");
217de1b686bSSascha Hauer 
218de1b686bSSascha Hauer 	return -1;
219de1b686bSSascha Hauer }
220de1b686bSSascha Hauer 
221de1b686bSSascha Hauer void eth_halt(void)
222de1b686bSSascha Hauer {
223de1b686bSSascha Hauer 	smc911x_reset();
224de1b686bSSascha Hauer }
225de1b686bSSascha Hauer 
226de1b686bSSascha Hauer int eth_rx(void)
227de1b686bSSascha Hauer {
228de1b686bSSascha Hauer 	u32 *data = (u32 *)NetRxPackets[0];
229de1b686bSSascha Hauer 	u32 pktlen, tmplen;
230de1b686bSSascha Hauer 	u32 status;
231de1b686bSSascha Hauer 
232890a02e8SStefan Roese 	if ((smc911x_reg_read(RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) {
233890a02e8SStefan Roese 		status = smc911x_reg_read(RX_STATUS_FIFO);
234de1b686bSSascha Hauer 		pktlen = (status & RX_STS_PKT_LEN) >> 16;
235de1b686bSSascha Hauer 
236890a02e8SStefan Roese 		smc911x_reg_write(RX_CFG, 0);
237de1b686bSSascha Hauer 
238de1b686bSSascha Hauer 		tmplen = (pktlen + 2+ 3) / 4;
239de1b686bSSascha Hauer 		while (tmplen--)
24033314470SNobuhiro Iwamatsu 			*data++ = pkt_data_pull(RX_DATA_FIFO);
241de1b686bSSascha Hauer 
242de1b686bSSascha Hauer 		if (status & RX_STS_ES)
243de1b686bSSascha Hauer 			printf(DRIVERNAME
244de1b686bSSascha Hauer 				": dropped bad packet. Status: 0x%08x\n",
245de1b686bSSascha Hauer 				status);
246de1b686bSSascha Hauer 		else
247de1b686bSSascha Hauer 			NetReceive(NetRxPackets[0], pktlen);
248de1b686bSSascha Hauer 	}
249de1b686bSSascha Hauer 
250de1b686bSSascha Hauer 	return 0;
251de1b686bSSascha Hauer }
252