xref: /openbmc/u-boot/drivers/net/smc911x.c (revision 2c0234fa)
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;
4203f3d8d3SMike Frysinger 	uchar m[6];
43de1b686bSSascha Hauer 
44*2c0234faSDaniel Mack 	if (eth_getenv_enetaddr("ethaddr", m)) {
45de1b686bSSascha Hauer 		/* if the environment has a valid mac address then use it */
46*2c0234faSDaniel Mack 		addrl = m[0] | (m[1] << 8) | (m[2] << 16) | (m[3] << 24);
47*2c0234faSDaniel Mack 		addrh = m[4] | (m[5] << 8);
48*2c0234faSDaniel Mack 		smc911x_set_mac_csr(ADDRL, addrl);
49*2c0234faSDaniel Mack 		smc911x_set_mac_csr(ADDRH, addrh);
50*2c0234faSDaniel Mack 	} else {
51de1b686bSSascha Hauer 		/* if not, try to get one from the eeprom */
52de1b686bSSascha Hauer 		addrh = smc911x_get_mac_csr(ADDRH);
53de1b686bSSascha Hauer 		addrl = smc911x_get_mac_csr(ADDRL);
54de1b686bSSascha Hauer 
55de1b686bSSascha Hauer 		m[0] = (addrl       ) & 0xff;
56de1b686bSSascha Hauer 		m[1] = (addrl >>  8 ) & 0xff;
57de1b686bSSascha Hauer 		m[2] = (addrl >> 16 ) & 0xff;
58de1b686bSSascha Hauer 		m[3] = (addrl >> 24 ) & 0xff;
59de1b686bSSascha Hauer 		m[4] = (addrh       ) & 0xff;
60de1b686bSSascha Hauer 		m[5] = (addrh >>  8 ) & 0xff;
61de1b686bSSascha Hauer 
62de1b686bSSascha Hauer 		/* we get 0xff when there is no eeprom connected */
63de1b686bSSascha Hauer 		if ((m[0] & m[1] & m[2] & m[3] & m[4] & m[5]) == 0xff) {
64de1b686bSSascha Hauer 			printf(DRIVERNAME ": no valid mac address in environment "
65de1b686bSSascha Hauer 				"and no eeprom found\n");
66de1b686bSSascha Hauer 			return -1;
67de1b686bSSascha Hauer 		}
6803f3d8d3SMike Frysinger 
6903f3d8d3SMike Frysinger 		eth_setenv_enetaddr("ethaddr", m);
70de1b686bSSascha Hauer 	}
71de1b686bSSascha Hauer 
7203f3d8d3SMike Frysinger 	printf(DRIVERNAME ": MAC %pM\n", m);
73de1b686bSSascha Hauer 
74de1b686bSSascha Hauer 	return 0;
75de1b686bSSascha Hauer }
76de1b686bSSascha Hauer 
77de1b686bSSascha Hauer static int smc911x_miiphy_read(u8 phy, u8 reg, u16 *val)
78de1b686bSSascha Hauer {
793e0f331cSGuennadi Liakhovetski 	while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
803e0f331cSGuennadi Liakhovetski 		;
81de1b686bSSascha Hauer 
82de1b686bSSascha Hauer 	smc911x_set_mac_csr(MII_ACC, phy << 11 | reg << 6 | MII_ACC_MII_BUSY);
83de1b686bSSascha Hauer 
843e0f331cSGuennadi Liakhovetski 	while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
853e0f331cSGuennadi Liakhovetski 		;
86de1b686bSSascha Hauer 
87de1b686bSSascha Hauer 	*val = smc911x_get_mac_csr(MII_DATA);
88de1b686bSSascha Hauer 
89de1b686bSSascha Hauer 	return 0;
90de1b686bSSascha Hauer }
91de1b686bSSascha Hauer 
92de1b686bSSascha Hauer static int smc911x_miiphy_write(u8 phy, u8 reg, u16  val)
93de1b686bSSascha Hauer {
943e0f331cSGuennadi Liakhovetski 	while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
953e0f331cSGuennadi Liakhovetski 		;
96de1b686bSSascha Hauer 
97de1b686bSSascha Hauer 	smc911x_set_mac_csr(MII_DATA, val);
98de1b686bSSascha Hauer 	smc911x_set_mac_csr(MII_ACC,
99de1b686bSSascha Hauer 		phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE);
100de1b686bSSascha Hauer 
1013e0f331cSGuennadi Liakhovetski 	while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
1023e0f331cSGuennadi Liakhovetski 		;
103de1b686bSSascha Hauer 	return 0;
104de1b686bSSascha Hauer }
105de1b686bSSascha Hauer 
106de1b686bSSascha Hauer static int smc911x_phy_reset(void)
107de1b686bSSascha Hauer {
108de1b686bSSascha Hauer 	u32 reg;
109de1b686bSSascha Hauer 
110890a02e8SStefan Roese 	reg = smc911x_reg_read(PMT_CTRL);
111de1b686bSSascha Hauer 	reg &= ~0xfffff030;
112de1b686bSSascha Hauer 	reg |= PMT_CTRL_PHY_RST;
113890a02e8SStefan Roese 	smc911x_reg_write(PMT_CTRL, reg);
114de1b686bSSascha Hauer 
115de1b686bSSascha Hauer 	mdelay(100);
116de1b686bSSascha Hauer 
117de1b686bSSascha Hauer 	return 0;
118de1b686bSSascha Hauer }
119de1b686bSSascha Hauer 
120de1b686bSSascha Hauer static void smc911x_phy_configure(void)
121de1b686bSSascha Hauer {
122de1b686bSSascha Hauer 	int timeout;
123de1b686bSSascha Hauer 	u16 status;
124de1b686bSSascha Hauer 
125de1b686bSSascha Hauer 	smc911x_phy_reset();
126de1b686bSSascha Hauer 
127de1b686bSSascha Hauer 	smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_RESET);
128de1b686bSSascha Hauer 	mdelay(1);
129de1b686bSSascha Hauer 	smc911x_miiphy_write(1, PHY_ANAR, 0x01e1);
130de1b686bSSascha Hauer 	smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG);
131de1b686bSSascha Hauer 
132de1b686bSSascha Hauer 	timeout = 5000;
133de1b686bSSascha Hauer 	do {
134de1b686bSSascha Hauer 		mdelay(1);
135de1b686bSSascha Hauer 		if ((timeout--) == 0)
136de1b686bSSascha Hauer 			goto err_out;
137de1b686bSSascha Hauer 
138de1b686bSSascha Hauer 		if (smc911x_miiphy_read(1, PHY_BMSR, &status) != 0)
139de1b686bSSascha Hauer 			goto err_out;
140de1b686bSSascha Hauer 	} while (!(status & PHY_BMSR_LS));
141de1b686bSSascha Hauer 
142de1b686bSSascha Hauer 	printf(DRIVERNAME ": phy initialized\n");
143de1b686bSSascha Hauer 
144de1b686bSSascha Hauer 	return;
145de1b686bSSascha Hauer 
146de1b686bSSascha Hauer err_out:
147de1b686bSSascha Hauer 	printf(DRIVERNAME ": autonegotiation timed out\n");
148de1b686bSSascha Hauer }
149de1b686bSSascha Hauer 
150de1b686bSSascha Hauer static void smc911x_enable(void)
151de1b686bSSascha Hauer {
152de1b686bSSascha Hauer 	/* Enable TX */
153890a02e8SStefan Roese 	smc911x_reg_write(HW_CFG, 8 << 16 | HW_CFG_SF);
154de1b686bSSascha Hauer 
155890a02e8SStefan Roese 	smc911x_reg_write(GPT_CFG, GPT_CFG_TIMER_EN | 10000);
156de1b686bSSascha Hauer 
157890a02e8SStefan Roese 	smc911x_reg_write(TX_CFG, TX_CFG_TX_ON);
158de1b686bSSascha Hauer 
159de1b686bSSascha Hauer 	/* no padding to start of packets */
160890a02e8SStefan Roese 	smc911x_reg_write(RX_CFG, 0);
161de1b686bSSascha Hauer 
162de1b686bSSascha Hauer 	smc911x_set_mac_csr(MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | MAC_CR_HBDIS);
163de1b686bSSascha Hauer 
164de1b686bSSascha Hauer }
165de1b686bSSascha Hauer 
166de1b686bSSascha Hauer int eth_init(bd_t *bd)
167de1b686bSSascha Hauer {
168de1b686bSSascha Hauer 	printf(DRIVERNAME ": initializing\n");
169de1b686bSSascha Hauer 
17075ba6d69SMike Frysinger 	if (smc911x_detect_chip())
171de1b686bSSascha Hauer 		goto err_out;
172de1b686bSSascha Hauer 
173de1b686bSSascha Hauer 	smc911x_reset();
174de1b686bSSascha Hauer 
175de1b686bSSascha Hauer 	/* Configure the PHY, initialize the link state */
176de1b686bSSascha Hauer 	smc911x_phy_configure();
177de1b686bSSascha Hauer 
178de1b686bSSascha Hauer 	if (smx911x_handle_mac_address(bd))
179de1b686bSSascha Hauer 		goto err_out;
180de1b686bSSascha Hauer 
181de1b686bSSascha Hauer 	/* Turn on Tx + Rx */
182de1b686bSSascha Hauer 	smc911x_enable();
183de1b686bSSascha Hauer 
184de1b686bSSascha Hauer 	return 0;
185de1b686bSSascha Hauer 
186de1b686bSSascha Hauer err_out:
187de1b686bSSascha Hauer 	return -1;
188de1b686bSSascha Hauer }
189de1b686bSSascha Hauer 
190de1b686bSSascha Hauer int eth_send(volatile void *packet, int length)
191de1b686bSSascha Hauer {
192de1b686bSSascha Hauer 	u32 *data = (u32*)packet;
193de1b686bSSascha Hauer 	u32 tmplen;
194de1b686bSSascha Hauer 	u32 status;
195de1b686bSSascha Hauer 
196890a02e8SStefan Roese 	smc911x_reg_write(TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG | TX_CMD_A_INT_LAST_SEG | length);
197890a02e8SStefan Roese 	smc911x_reg_write(TX_DATA_FIFO, length);
198de1b686bSSascha Hauer 
199de1b686bSSascha Hauer 	tmplen = (length + 3) / 4;
200de1b686bSSascha Hauer 
201de1b686bSSascha Hauer 	while (tmplen--)
20233314470SNobuhiro Iwamatsu 		pkt_data_push(TX_DATA_FIFO, *data++);
203de1b686bSSascha Hauer 
204de1b686bSSascha Hauer 	/* wait for transmission */
205890a02e8SStefan Roese 	while (!((smc911x_reg_read(TX_FIFO_INF) & TX_FIFO_INF_TSUSED) >> 16));
206de1b686bSSascha Hauer 
207de1b686bSSascha Hauer 	/* get status. Ignore 'no carrier' error, it has no meaning for
208de1b686bSSascha Hauer 	 * full duplex operation
209de1b686bSSascha Hauer 	 */
210890a02e8SStefan Roese 	status = smc911x_reg_read(TX_STATUS_FIFO) & (TX_STS_LOC | TX_STS_LATE_COLL |
211de1b686bSSascha Hauer 		TX_STS_MANY_COLL | TX_STS_MANY_DEFER | TX_STS_UNDERRUN);
212de1b686bSSascha Hauer 
213de1b686bSSascha Hauer 	if (!status)
214de1b686bSSascha Hauer 		return 0;
215de1b686bSSascha Hauer 
216de1b686bSSascha Hauer 	printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n",
217de1b686bSSascha Hauer 		status & TX_STS_LOC ? "TX_STS_LOC " : "",
218de1b686bSSascha Hauer 		status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "",
219de1b686bSSascha Hauer 		status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "",
220de1b686bSSascha Hauer 		status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "",
221de1b686bSSascha Hauer 		status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : "");
222de1b686bSSascha Hauer 
223de1b686bSSascha Hauer 	return -1;
224de1b686bSSascha Hauer }
225de1b686bSSascha Hauer 
226de1b686bSSascha Hauer void eth_halt(void)
227de1b686bSSascha Hauer {
228de1b686bSSascha Hauer 	smc911x_reset();
229de1b686bSSascha Hauer }
230de1b686bSSascha Hauer 
231de1b686bSSascha Hauer int eth_rx(void)
232de1b686bSSascha Hauer {
233de1b686bSSascha Hauer 	u32 *data = (u32 *)NetRxPackets[0];
234de1b686bSSascha Hauer 	u32 pktlen, tmplen;
235de1b686bSSascha Hauer 	u32 status;
236de1b686bSSascha Hauer 
237890a02e8SStefan Roese 	if ((smc911x_reg_read(RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) {
238890a02e8SStefan Roese 		status = smc911x_reg_read(RX_STATUS_FIFO);
239de1b686bSSascha Hauer 		pktlen = (status & RX_STS_PKT_LEN) >> 16;
240de1b686bSSascha Hauer 
241890a02e8SStefan Roese 		smc911x_reg_write(RX_CFG, 0);
242de1b686bSSascha Hauer 
243de1b686bSSascha Hauer 		tmplen = (pktlen + 2+ 3) / 4;
244de1b686bSSascha Hauer 		while (tmplen--)
24533314470SNobuhiro Iwamatsu 			*data++ = pkt_data_pull(RX_DATA_FIFO);
246de1b686bSSascha Hauer 
247de1b686bSSascha Hauer 		if (status & RX_STS_ES)
248de1b686bSSascha Hauer 			printf(DRIVERNAME
249de1b686bSSascha Hauer 				": dropped bad packet. Status: 0x%08x\n",
250de1b686bSSascha Hauer 				status);
251de1b686bSSascha Hauer 		else
252de1b686bSSascha Hauer 			NetReceive(NetRxPackets[0], pktlen);
253de1b686bSSascha Hauer 	}
254de1b686bSSascha Hauer 
255de1b686bSSascha Hauer 	return 0;
256de1b686bSSascha Hauer }
257