blob: ceb4f81215212857c1c9830244f16684d6bfb82b [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Sascha Hauerf43a42d2008-04-15 00:08:20 -04002/*
3 * SMSC LAN9[12]1[567] Network driver
4 *
Stelian Pop50497522008-05-08 22:52:09 +02005 * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
Sascha Hauerf43a42d2008-04-15 00:08:20 -04006 */
7
8#include <common.h>
Sascha Hauerf43a42d2008-04-15 00:08:20 -04009#include <command.h>
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070010#include <malloc.h>
Sascha Hauerf43a42d2008-04-15 00:08:20 -040011#include <net.h>
12#include <miiphy.h>
13
Mike Frysingerad8e4f42009-02-23 10:29:47 -050014#include "smc911x.h"
Sascha Hauerf43a42d2008-04-15 00:08:20 -040015
Mike Rapoport0b3cc382009-11-11 10:03:09 +020016static void smc911x_handle_mac_address(struct eth_device *dev)
Sascha Hauerf43a42d2008-04-15 00:08:20 -040017{
18 unsigned long addrh, addrl;
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070019 uchar *m = dev->enetaddr;
Sascha Hauerf43a42d2008-04-15 00:08:20 -040020
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070021 addrl = m[0] | (m[1] << 8) | (m[2] << 16) | (m[3] << 24);
22 addrh = m[4] | (m[5] << 8);
23 smc911x_set_mac_csr(dev, ADDRL, addrl);
24 smc911x_set_mac_csr(dev, ADDRH, addrh);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040025
Mike Frysinger719b6752009-02-11 19:09:54 -050026 printf(DRIVERNAME ": MAC %pM\n", m);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040027}
28
Helmut Raigerbe8f9c02011-06-29 00:12:14 +000029static int smc911x_eth_phy_read(struct eth_device *dev,
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070030 u8 phy, u8 reg, u16 *val)
Sascha Hauerf43a42d2008-04-15 00:08:20 -040031{
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070032 while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY)
Guennadi Liakhovetski588bd2b2008-04-29 12:35:08 +000033 ;
Sascha Hauerf43a42d2008-04-15 00:08:20 -040034
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070035 smc911x_set_mac_csr(dev, MII_ACC, phy << 11 | reg << 6 |
36 MII_ACC_MII_BUSY);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040037
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070038 while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY)
Guennadi Liakhovetski588bd2b2008-04-29 12:35:08 +000039 ;
Sascha Hauerf43a42d2008-04-15 00:08:20 -040040
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070041 *val = smc911x_get_mac_csr(dev, MII_DATA);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040042
43 return 0;
44}
45
Helmut Raigerbe8f9c02011-06-29 00:12:14 +000046static int smc911x_eth_phy_write(struct eth_device *dev,
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070047 u8 phy, u8 reg, u16 val)
Sascha Hauerf43a42d2008-04-15 00:08:20 -040048{
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070049 while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY)
Guennadi Liakhovetski588bd2b2008-04-29 12:35:08 +000050 ;
Sascha Hauerf43a42d2008-04-15 00:08:20 -040051
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070052 smc911x_set_mac_csr(dev, MII_DATA, val);
53 smc911x_set_mac_csr(dev, MII_ACC,
Sascha Hauerf43a42d2008-04-15 00:08:20 -040054 phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE);
55
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070056 while (smc911x_get_mac_csr(dev, MII_ACC) & MII_ACC_MII_BUSY)
Guennadi Liakhovetski588bd2b2008-04-29 12:35:08 +000057 ;
Sascha Hauerf43a42d2008-04-15 00:08:20 -040058 return 0;
59}
60
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070061static int smc911x_phy_reset(struct eth_device *dev)
Sascha Hauerf43a42d2008-04-15 00:08:20 -040062{
63 u32 reg;
64
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070065 reg = smc911x_reg_read(dev, PMT_CTRL);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040066 reg &= ~0xfffff030;
67 reg |= PMT_CTRL_PHY_RST;
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070068 smc911x_reg_write(dev, PMT_CTRL, reg);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040069
70 mdelay(100);
71
72 return 0;
73}
74
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070075static void smc911x_phy_configure(struct eth_device *dev)
Sascha Hauerf43a42d2008-04-15 00:08:20 -040076{
77 int timeout;
78 u16 status;
79
Ben Warrenfbfdd3a2009-07-20 22:01:11 -070080 smc911x_phy_reset(dev);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040081
Helmut Raigerbe8f9c02011-06-29 00:12:14 +000082 smc911x_eth_phy_write(dev, 1, MII_BMCR, BMCR_RESET);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040083 mdelay(1);
Helmut Raigerbe8f9c02011-06-29 00:12:14 +000084 smc911x_eth_phy_write(dev, 1, MII_ADVERTISE, 0x01e1);
85 smc911x_eth_phy_write(dev, 1, MII_BMCR, BMCR_ANENABLE |
Mike Frysingerd63ee712010-12-23 15:40:12 -050086 BMCR_ANRESTART);
Sascha Hauerf43a42d2008-04-15 00:08:20 -040087
88 timeout = 5000;
89 do {
90 mdelay(1);
91 if ((timeout--) == 0)
92 goto err_out;
93
Helmut Raigerbe8f9c02011-06-29 00:12:14 +000094 if (smc911x_eth_phy_read(dev, 1, MII_BMSR, &status) != 0)
Sascha Hauerf43a42d2008-04-15 00:08:20 -040095 goto err_out;
Mike Frysingerd63ee712010-12-23 15:40:12 -050096 } while (!(status & BMSR_LSTATUS));
Sascha Hauerf43a42d2008-04-15 00:08:20 -040097
98 printf(DRIVERNAME ": phy initialized\n");
99
100 return;
101
102err_out:
103 printf(DRIVERNAME ": autonegotiation timed out\n");
104}
105
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700106static void smc911x_enable(struct eth_device *dev)
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400107{
108 /* Enable TX */
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700109 smc911x_reg_write(dev, HW_CFG, 8 << 16 | HW_CFG_SF);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400110
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700111 smc911x_reg_write(dev, GPT_CFG, GPT_CFG_TIMER_EN | 10000);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400112
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700113 smc911x_reg_write(dev, TX_CFG, TX_CFG_TX_ON);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400114
115 /* no padding to start of packets */
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700116 smc911x_reg_write(dev, RX_CFG, 0);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400117
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700118 smc911x_set_mac_csr(dev, MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN |
119 MAC_CR_HBDIS);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400120
121}
122
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700123static int smc911x_init(struct eth_device *dev, bd_t * bd)
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400124{
Olof Johansson64aabc22009-09-29 10:21:29 -0400125 struct chip_id *id = dev->priv;
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400126
Wolfgang Denkd61fbcc2009-10-28 00:49:47 +0100127 printf(DRIVERNAME ": detected %s controller\n", id->name);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400128
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700129 smc911x_reset(dev);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400130
131 /* Configure the PHY, initialize the link state */
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700132 smc911x_phy_configure(dev);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400133
Mike Rapoport0b3cc382009-11-11 10:03:09 +0200134 smc911x_handle_mac_address(dev);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400135
136 /* Turn on Tx + Rx */
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700137 smc911x_enable(dev);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400138
139 return 0;
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400140}
141
Anatolij Gustschin8c6f60a2012-05-20 12:22:55 +0000142static int smc911x_send(struct eth_device *dev, void *packet, int length)
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400143{
144 u32 *data = (u32*)packet;
145 u32 tmplen;
146 u32 status;
147
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700148 smc911x_reg_write(dev, TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG |
149 TX_CMD_A_INT_LAST_SEG | length);
150 smc911x_reg_write(dev, TX_DATA_FIFO, length);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400151
152 tmplen = (length + 3) / 4;
153
Guennadi Liakhovetski588bd2b2008-04-29 12:35:08 +0000154 while (tmplen--)
Marek Vasut433f0ea2020-03-15 00:08:31 +0100155 smc911x_reg_write(dev, TX_DATA_FIFO, *data++);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400156
157 /* wait for transmission */
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700158 while (!((smc911x_reg_read(dev, TX_FIFO_INF) &
159 TX_FIFO_INF_TSUSED) >> 16));
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400160
161 /* get status. Ignore 'no carrier' error, it has no meaning for
162 * full duplex operation
163 */
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700164 status = smc911x_reg_read(dev, TX_STATUS_FIFO) &
165 (TX_STS_LOC | TX_STS_LATE_COLL | TX_STS_MANY_COLL |
166 TX_STS_MANY_DEFER | TX_STS_UNDERRUN);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400167
Guennadi Liakhovetski588bd2b2008-04-29 12:35:08 +0000168 if (!status)
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400169 return 0;
170
171 printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n",
172 status & TX_STS_LOC ? "TX_STS_LOC " : "",
173 status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "",
174 status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "",
175 status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "",
176 status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : "");
177
178 return -1;
179}
180
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700181static void smc911x_halt(struct eth_device *dev)
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400182{
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700183 smc911x_reset(dev);
Marek Vasutc5794ed2014-10-11 18:42:58 +0200184 smc911x_handle_mac_address(dev);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400185}
186
Marek Vasut84358cf2020-03-15 15:40:15 +0100187static int smc911x_recv(struct eth_device *dev)
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400188{
Joe Hershberger9f09a362015-04-08 01:41:06 -0500189 u32 *data = (u32 *)net_rx_packets[0];
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400190 u32 pktlen, tmplen;
191 u32 status;
192
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700193 if ((smc911x_reg_read(dev, RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) {
194 status = smc911x_reg_read(dev, RX_STATUS_FIFO);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400195 pktlen = (status & RX_STS_PKT_LEN) >> 16;
196
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700197 smc911x_reg_write(dev, RX_CFG, 0);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400198
Valentin Yakovenkov8cb08012010-04-23 09:40:23 +0400199 tmplen = (pktlen + 3) / 4;
Guennadi Liakhovetski588bd2b2008-04-29 12:35:08 +0000200 while (tmplen--)
Marek Vasut433f0ea2020-03-15 00:08:31 +0100201 *data++ = smc911x_reg_read(dev, RX_DATA_FIFO);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400202
Guennadi Liakhovetski588bd2b2008-04-29 12:35:08 +0000203 if (status & RX_STS_ES)
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400204 printf(DRIVERNAME
205 ": dropped bad packet. Status: 0x%08x\n",
206 status);
207 else
Joe Hershberger9f09a362015-04-08 01:41:06 -0500208 net_process_received_packet(net_rx_packets[0], pktlen);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400209 }
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700210
211 return 0;
212}
213
Helmut Raigerbe8f9c02011-06-29 00:12:14 +0000214#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
215/* wrapper for smc911x_eth_phy_read */
Joe Hershberger1fbcbed2016-08-08 11:28:38 -0500216static int smc911x_miiphy_read(struct mii_dev *bus, int phy, int devad,
217 int reg)
Helmut Raigerbe8f9c02011-06-29 00:12:14 +0000218{
Joe Hershberger1fbcbed2016-08-08 11:28:38 -0500219 struct eth_device *dev = eth_get_dev_by_name(bus->name);
Marek Vasut14330e52020-03-15 15:43:20 +0100220 u16 val = 0;
221 int ret;
222
223 if (!dev)
224 return -ENODEV;
225
226 ret = smc911x_eth_phy_read(dev, phy, reg, &val);
227 if (ret < 0)
228 return ret;
229
230 return val;
Helmut Raigerbe8f9c02011-06-29 00:12:14 +0000231}
232/* wrapper for smc911x_eth_phy_write */
Joe Hershberger1fbcbed2016-08-08 11:28:38 -0500233static int smc911x_miiphy_write(struct mii_dev *bus, int phy, int devad,
234 int reg, u16 val)
Helmut Raigerbe8f9c02011-06-29 00:12:14 +0000235{
Joe Hershberger1fbcbed2016-08-08 11:28:38 -0500236 struct eth_device *dev = eth_get_dev_by_name(bus->name);
Marek Vasut14330e52020-03-15 15:43:20 +0100237
238 if (!dev)
239 return -ENODEV;
240
241 return smc911x_eth_phy_write(dev, phy, reg, val);
Helmut Raigerbe8f9c02011-06-29 00:12:14 +0000242}
243#endif
244
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700245int smc911x_initialize(u8 dev_num, int base_addr)
246{
247 unsigned long addrl, addrh;
248 struct eth_device *dev;
249
Marek Vasutcf613592020-03-15 15:14:18 +0100250 dev = calloc(1, sizeof(*dev));
251 if (!dev)
252 return -ENOMEM;
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700253
254 dev->iobase = base_addr;
255
Steve Sakomanb3ec49d2009-10-20 18:21:18 +0200256 /* Try to detect chip. Will fail if not present. */
257 if (smc911x_detect_chip(dev)) {
258 free(dev);
259 return 0;
260 }
261
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700262 addrh = smc911x_get_mac_csr(dev, ADDRH);
263 addrl = smc911x_get_mac_csr(dev, ADDRL);
Seunghyeon Rheea8572f12009-11-02 00:00:00 -0800264 if (!(addrl == 0xffffffff && addrh == 0x0000ffff)) {
265 /* address is obtained from optional eeprom */
266 dev->enetaddr[0] = addrl;
267 dev->enetaddr[1] = addrl >> 8;
268 dev->enetaddr[2] = addrl >> 16;
269 dev->enetaddr[3] = addrl >> 24;
270 dev->enetaddr[4] = addrh;
271 dev->enetaddr[5] = addrh >> 8;
272 }
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700273
274 dev->init = smc911x_init;
275 dev->halt = smc911x_halt;
276 dev->send = smc911x_send;
Marek Vasut84358cf2020-03-15 15:40:15 +0100277 dev->recv = smc911x_recv;
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700278 sprintf(dev->name, "%s-%hu", DRIVERNAME, dev_num);
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400279
Ben Warrenfbfdd3a2009-07-20 22:01:11 -0700280 eth_register(dev);
Helmut Raigerbe8f9c02011-06-29 00:12:14 +0000281
282#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
Joe Hershberger1fbcbed2016-08-08 11:28:38 -0500283 int retval;
284 struct mii_dev *mdiodev = mdio_alloc();
285 if (!mdiodev)
286 return -ENOMEM;
287 strncpy(mdiodev->name, dev->name, MDIO_NAME_LEN);
288 mdiodev->read = smc911x_miiphy_read;
289 mdiodev->write = smc911x_miiphy_write;
290
291 retval = mdio_register(mdiodev);
292 if (retval < 0)
293 return retval;
Helmut Raigerbe8f9c02011-06-29 00:12:14 +0000294#endif
295
Mike Rapoport907975a2009-11-12 15:35:08 +0200296 return 1;
Sascha Hauerf43a42d2008-04-15 00:08:20 -0400297}