blob: 7b79831c2814d375ee3eceb95c8c397aa7652986 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Sascha Hauerde1b6862008-04-15 00:08:20 -04002/*
3 * SMSC LAN9[12]1[567] Network driver
4 *
Stelian Popcce9cfd2008-05-08 22:52:09 +02005 * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
Sascha Hauerde1b6862008-04-15 00:08:20 -04006 */
7
8#include <common.h>
Sascha Hauerde1b6862008-04-15 00:08:20 -04009#include <command.h>
Ben Warren736fead2009-07-20 22:01:11 -070010#include <malloc.h>
Sascha Hauerde1b6862008-04-15 00:08:20 -040011#include <net.h>
12#include <miiphy.h>
Marek Vasutba267c72020-03-15 14:42:23 +010013#include <linux/io.h>
Simon Glassc05ed002020-05-10 11:40:11 -060014#include <linux/delay.h>
Marek Vasuteb46efa2020-03-15 15:03:07 +010015#include <linux/types.h>
Sascha Hauerde1b6862008-04-15 00:08:20 -040016
Mike Frysinger75ba6d62009-02-23 10:29:47 -050017#include "smc911x.h"
Sascha Hauerde1b6862008-04-15 00:08:20 -040018
Marek Vasuteb46efa2020-03-15 15:03:07 +010019struct chip_id {
20 u16 id;
21 char *name;
22};
23
Marek Vasut3dbab922020-03-15 15:36:09 +010024struct smc911x_priv {
Marek Vasut81486932020-03-15 17:39:01 +010025#ifndef CONFIG_DM_ETH
Marek Vasut3dbab922020-03-15 15:36:09 +010026 struct eth_device dev;
Marek Vasut81486932020-03-15 17:39:01 +010027#endif
Marek Vasut3dbab922020-03-15 15:36:09 +010028 phys_addr_t iobase;
29 const struct chip_id *chipid;
30 unsigned char enetaddr[6];
31};
32
Marek Vasuteb46efa2020-03-15 15:03:07 +010033static const struct chip_id chip_ids[] = {
34 { CHIP_89218, "LAN89218" },
35 { CHIP_9115, "LAN9115" },
36 { CHIP_9116, "LAN9116" },
37 { CHIP_9117, "LAN9117" },
38 { CHIP_9118, "LAN9118" },
39 { CHIP_9211, "LAN9211" },
40 { CHIP_9215, "LAN9215" },
41 { CHIP_9216, "LAN9216" },
42 { CHIP_9217, "LAN9217" },
43 { CHIP_9218, "LAN9218" },
44 { CHIP_9220, "LAN9220" },
45 { CHIP_9221, "LAN9221" },
46 { 0, NULL },
47};
48
49#define DRIVERNAME "smc911x"
50
51#if defined (CONFIG_SMC911X_32_BIT) && \
52 defined (CONFIG_SMC911X_16_BIT)
53#error "SMC911X: Only one of CONFIG_SMC911X_32_BIT and \
54 CONFIG_SMC911X_16_BIT shall be set"
55#endif
56
57#if defined (CONFIG_SMC911X_32_BIT)
Marek Vasut3dbab922020-03-15 15:36:09 +010058static u32 smc911x_reg_read(struct smc911x_priv *priv, u32 offset)
Marek Vasuteb46efa2020-03-15 15:03:07 +010059{
Marek Vasut3dbab922020-03-15 15:36:09 +010060 return readl(priv->iobase + offset);
Marek Vasuteb46efa2020-03-15 15:03:07 +010061}
Marek Vasuteb46efa2020-03-15 15:03:07 +010062
Marek Vasut3dbab922020-03-15 15:36:09 +010063static void smc911x_reg_write(struct smc911x_priv *priv, u32 offset, u32 val)
Marek Vasuteb46efa2020-03-15 15:03:07 +010064{
Marek Vasut3dbab922020-03-15 15:36:09 +010065 writel(val, priv->iobase + offset);
Marek Vasuteb46efa2020-03-15 15:03:07 +010066}
Marek Vasuteb46efa2020-03-15 15:03:07 +010067#elif defined (CONFIG_SMC911X_16_BIT)
Marek Vasut3dbab922020-03-15 15:36:09 +010068static u32 smc911x_reg_read(struct smc911x_priv *priv, u32 offset)
Marek Vasuteb46efa2020-03-15 15:03:07 +010069{
Marek Vasut3dbab922020-03-15 15:36:09 +010070 return (readw(priv->iobase + offset) & 0xffff) |
71 (readw(priv->iobase + offset + 2) << 16);
Marek Vasuteb46efa2020-03-15 15:03:07 +010072}
Marek Vasut3dbab922020-03-15 15:36:09 +010073static void smc911x_reg_write(struct smc911x_priv *priv, u32 offset, u32 val)
Marek Vasuteb46efa2020-03-15 15:03:07 +010074{
Marek Vasut3dbab922020-03-15 15:36:09 +010075 writew(val & 0xffff, priv->iobase + offset);
76 writew(val >> 16, priv->iobase + offset + 2);
Marek Vasuteb46efa2020-03-15 15:03:07 +010077}
78#else
79#error "SMC911X: undefined bus width"
80#endif /* CONFIG_SMC911X_16_BIT */
81
Marek Vasut3dbab922020-03-15 15:36:09 +010082static u32 smc911x_get_mac_csr(struct smc911x_priv *priv, u8 reg)
Marek Vasuteb46efa2020-03-15 15:03:07 +010083{
Marek Vasut3dbab922020-03-15 15:36:09 +010084 while (smc911x_reg_read(priv, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY)
Marek Vasuteb46efa2020-03-15 15:03:07 +010085 ;
Marek Vasut3dbab922020-03-15 15:36:09 +010086 smc911x_reg_write(priv, MAC_CSR_CMD,
Marek Vasuteb46efa2020-03-15 15:03:07 +010087 MAC_CSR_CMD_CSR_BUSY | MAC_CSR_CMD_R_NOT_W | reg);
Marek Vasut3dbab922020-03-15 15:36:09 +010088 while (smc911x_reg_read(priv, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY)
Marek Vasuteb46efa2020-03-15 15:03:07 +010089 ;
90
Marek Vasut3dbab922020-03-15 15:36:09 +010091 return smc911x_reg_read(priv, MAC_CSR_DATA);
Marek Vasuteb46efa2020-03-15 15:03:07 +010092}
93
Marek Vasut3dbab922020-03-15 15:36:09 +010094static void smc911x_set_mac_csr(struct smc911x_priv *priv, u8 reg, u32 data)
Marek Vasuteb46efa2020-03-15 15:03:07 +010095{
Marek Vasut3dbab922020-03-15 15:36:09 +010096 while (smc911x_reg_read(priv, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY)
Marek Vasuteb46efa2020-03-15 15:03:07 +010097 ;
Marek Vasut3dbab922020-03-15 15:36:09 +010098 smc911x_reg_write(priv, MAC_CSR_DATA, data);
99 smc911x_reg_write(priv, MAC_CSR_CMD, MAC_CSR_CMD_CSR_BUSY | reg);
100 while (smc911x_reg_read(priv, MAC_CSR_CMD) & MAC_CSR_CMD_CSR_BUSY)
Marek Vasuteb46efa2020-03-15 15:03:07 +0100101 ;
102}
103
Marek Vasut3dbab922020-03-15 15:36:09 +0100104static int smc911x_detect_chip(struct smc911x_priv *priv)
Marek Vasuteb46efa2020-03-15 15:03:07 +0100105{
106 unsigned long val, i;
107
Marek Vasut3dbab922020-03-15 15:36:09 +0100108 val = smc911x_reg_read(priv, BYTE_TEST);
Marek Vasuteb46efa2020-03-15 15:03:07 +0100109 if (val == 0xffffffff) {
110 /* Special case -- no chip present */
111 return -1;
112 } else if (val != 0x87654321) {
113 printf(DRIVERNAME ": Invalid chip endian 0x%08lx\n", val);
114 return -1;
115 }
116
Marek Vasut3dbab922020-03-15 15:36:09 +0100117 val = smc911x_reg_read(priv, ID_REV) >> 16;
Marek Vasuteb46efa2020-03-15 15:03:07 +0100118 for (i = 0; chip_ids[i].id != 0; i++) {
119 if (chip_ids[i].id == val) break;
120 }
121 if (!chip_ids[i].id) {
122 printf(DRIVERNAME ": Unknown chip ID %04lx\n", val);
123 return -1;
124 }
125
Marek Vasut3dbab922020-03-15 15:36:09 +0100126 priv->chipid = &chip_ids[i];
Marek Vasuteb46efa2020-03-15 15:03:07 +0100127
128 return 0;
129}
130
Marek Vasut3dbab922020-03-15 15:36:09 +0100131static void smc911x_reset(struct smc911x_priv *priv)
Marek Vasuteb46efa2020-03-15 15:03:07 +0100132{
133 int timeout;
134
135 /*
136 * Take out of PM setting first
137 * Device is already wake up if PMT_CTRL_READY bit is set
138 */
Marek Vasut3dbab922020-03-15 15:36:09 +0100139 if ((smc911x_reg_read(priv, PMT_CTRL) & PMT_CTRL_READY) == 0) {
Marek Vasuteb46efa2020-03-15 15:03:07 +0100140 /* Write to the bytetest will take out of powerdown */
Marek Vasut3dbab922020-03-15 15:36:09 +0100141 smc911x_reg_write(priv, BYTE_TEST, 0x0);
Marek Vasuteb46efa2020-03-15 15:03:07 +0100142
143 timeout = 10;
144
145 while (timeout-- &&
Marek Vasut3dbab922020-03-15 15:36:09 +0100146 !(smc911x_reg_read(priv, PMT_CTRL) & PMT_CTRL_READY))
Marek Vasuteb46efa2020-03-15 15:03:07 +0100147 udelay(10);
148 if (timeout < 0) {
149 printf(DRIVERNAME
150 ": timeout waiting for PM restore\n");
151 return;
152 }
153 }
154
155 /* Disable interrupts */
Marek Vasut3dbab922020-03-15 15:36:09 +0100156 smc911x_reg_write(priv, INT_EN, 0);
Marek Vasuteb46efa2020-03-15 15:03:07 +0100157
Marek Vasut3dbab922020-03-15 15:36:09 +0100158 smc911x_reg_write(priv, HW_CFG, HW_CFG_SRST);
Marek Vasuteb46efa2020-03-15 15:03:07 +0100159
160 timeout = 1000;
Marek Vasut3dbab922020-03-15 15:36:09 +0100161 while (timeout-- && smc911x_reg_read(priv, E2P_CMD) & E2P_CMD_EPC_BUSY)
Marek Vasuteb46efa2020-03-15 15:03:07 +0100162 udelay(10);
163
164 if (timeout < 0) {
165 printf(DRIVERNAME ": reset timeout\n");
166 return;
167 }
168
169 /* Reset the FIFO level and flow control settings */
Marek Vasut3dbab922020-03-15 15:36:09 +0100170 smc911x_set_mac_csr(priv, FLOW, FLOW_FCPT | FLOW_FCEN);
171 smc911x_reg_write(priv, AFC_CFG, 0x0050287F);
Marek Vasuteb46efa2020-03-15 15:03:07 +0100172
173 /* Set to LED outputs */
Marek Vasut3dbab922020-03-15 15:36:09 +0100174 smc911x_reg_write(priv, GPIO_CFG, 0x70070000);
Marek Vasuteb46efa2020-03-15 15:03:07 +0100175}
176
Marek Vasut3dbab922020-03-15 15:36:09 +0100177static void smc911x_handle_mac_address(struct smc911x_priv *priv)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400178{
179 unsigned long addrh, addrl;
Marek Vasut3dbab922020-03-15 15:36:09 +0100180 unsigned char *m = priv->enetaddr;
Sascha Hauerde1b6862008-04-15 00:08:20 -0400181
Ben Warren736fead2009-07-20 22:01:11 -0700182 addrl = m[0] | (m[1] << 8) | (m[2] << 16) | (m[3] << 24);
183 addrh = m[4] | (m[5] << 8);
Marek Vasut3dbab922020-03-15 15:36:09 +0100184 smc911x_set_mac_csr(priv, ADDRL, addrl);
185 smc911x_set_mac_csr(priv, ADDRH, addrh);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400186
Mike Frysinger03f3d8d2009-02-11 19:09:54 -0500187 printf(DRIVERNAME ": MAC %pM\n", m);
Ben Warren736fead2009-07-20 22:01:11 -0700188}
189
Andre Przywara117a52e2020-06-11 12:03:17 +0100190static bool smc911x_read_mac_address(struct smc911x_priv *priv)
191{
192 u32 addrh, addrl;
193
194 /* address is obtained from optional eeprom */
195 addrh = smc911x_get_mac_csr(priv, ADDRH);
196 addrl = smc911x_get_mac_csr(priv, ADDRL);
197 if (addrl == 0xffffffff && addrh == 0x0000ffff)
198 return false;
199
200 priv->enetaddr[0] = addrl;
201 priv->enetaddr[1] = addrl >> 8;
202 priv->enetaddr[2] = addrl >> 16;
203 priv->enetaddr[3] = addrl >> 24;
204 priv->enetaddr[4] = addrh;
205 priv->enetaddr[5] = addrh >> 8;
206
207 return true;
208}
209
Marek Vasut3dbab922020-03-15 15:36:09 +0100210static int smc911x_eth_phy_read(struct smc911x_priv *priv,
Ben Warren736fead2009-07-20 22:01:11 -0700211 u8 phy, u8 reg, u16 *val)
212{
Marek Vasut3dbab922020-03-15 15:36:09 +0100213 while (smc911x_get_mac_csr(priv, MII_ACC) & MII_ACC_MII_BUSY)
Ben Warren736fead2009-07-20 22:01:11 -0700214 ;
215
Marek Vasut3dbab922020-03-15 15:36:09 +0100216 smc911x_set_mac_csr(priv, MII_ACC, phy << 11 | reg << 6 |
Ben Warren736fead2009-07-20 22:01:11 -0700217 MII_ACC_MII_BUSY);
218
Marek Vasut3dbab922020-03-15 15:36:09 +0100219 while (smc911x_get_mac_csr(priv, MII_ACC) & MII_ACC_MII_BUSY)
Ben Warren736fead2009-07-20 22:01:11 -0700220 ;
221
Marek Vasut3dbab922020-03-15 15:36:09 +0100222 *val = smc911x_get_mac_csr(priv, MII_DATA);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400223
224 return 0;
225}
226
Marek Vasut3dbab922020-03-15 15:36:09 +0100227static int smc911x_eth_phy_write(struct smc911x_priv *priv,
Ben Warren736fead2009-07-20 22:01:11 -0700228 u8 phy, u8 reg, u16 val)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400229{
Marek Vasut3dbab922020-03-15 15:36:09 +0100230 while (smc911x_get_mac_csr(priv, MII_ACC) & MII_ACC_MII_BUSY)
Guennadi Liakhovetski3e0f3312008-04-29 12:35:08 +0000231 ;
Sascha Hauerde1b6862008-04-15 00:08:20 -0400232
Marek Vasut3dbab922020-03-15 15:36:09 +0100233 smc911x_set_mac_csr(priv, MII_DATA, val);
234 smc911x_set_mac_csr(priv, MII_ACC,
Sascha Hauerde1b6862008-04-15 00:08:20 -0400235 phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE);
236
Marek Vasut3dbab922020-03-15 15:36:09 +0100237 while (smc911x_get_mac_csr(priv, MII_ACC) & MII_ACC_MII_BUSY)
Guennadi Liakhovetski3e0f3312008-04-29 12:35:08 +0000238 ;
Sascha Hauerde1b6862008-04-15 00:08:20 -0400239 return 0;
240}
241
Marek Vasut3dbab922020-03-15 15:36:09 +0100242static int smc911x_phy_reset(struct smc911x_priv *priv)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400243{
244 u32 reg;
245
Marek Vasut3dbab922020-03-15 15:36:09 +0100246 reg = smc911x_reg_read(priv, PMT_CTRL);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400247 reg &= ~0xfffff030;
248 reg |= PMT_CTRL_PHY_RST;
Marek Vasut3dbab922020-03-15 15:36:09 +0100249 smc911x_reg_write(priv, PMT_CTRL, reg);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400250
251 mdelay(100);
252
253 return 0;
254}
255
Marek Vasut3dbab922020-03-15 15:36:09 +0100256static void smc911x_phy_configure(struct smc911x_priv *priv)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400257{
258 int timeout;
259 u16 status;
260
Marek Vasut3dbab922020-03-15 15:36:09 +0100261 smc911x_phy_reset(priv);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400262
Marek Vasut3dbab922020-03-15 15:36:09 +0100263 smc911x_eth_phy_write(priv, 1, MII_BMCR, BMCR_RESET);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400264 mdelay(1);
Marek Vasut3dbab922020-03-15 15:36:09 +0100265 smc911x_eth_phy_write(priv, 1, MII_ADVERTISE, 0x01e1);
266 smc911x_eth_phy_write(priv, 1, MII_BMCR, BMCR_ANENABLE |
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500267 BMCR_ANRESTART);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400268
269 timeout = 5000;
270 do {
271 mdelay(1);
272 if ((timeout--) == 0)
273 goto err_out;
274
Marek Vasut3dbab922020-03-15 15:36:09 +0100275 if (smc911x_eth_phy_read(priv, 1, MII_BMSR, &status) != 0)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400276 goto err_out;
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500277 } while (!(status & BMSR_LSTATUS));
Sascha Hauerde1b6862008-04-15 00:08:20 -0400278
279 printf(DRIVERNAME ": phy initialized\n");
280
281 return;
282
283err_out:
284 printf(DRIVERNAME ": autonegotiation timed out\n");
285}
286
Marek Vasut3dbab922020-03-15 15:36:09 +0100287static void smc911x_enable(struct smc911x_priv *priv)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400288{
289 /* Enable TX */
Marek Vasut3dbab922020-03-15 15:36:09 +0100290 smc911x_reg_write(priv, HW_CFG, 8 << 16 | HW_CFG_SF);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400291
Marek Vasut3dbab922020-03-15 15:36:09 +0100292 smc911x_reg_write(priv, GPT_CFG, GPT_CFG_TIMER_EN | 10000);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400293
Marek Vasut3dbab922020-03-15 15:36:09 +0100294 smc911x_reg_write(priv, TX_CFG, TX_CFG_TX_ON);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400295
296 /* no padding to start of packets */
Marek Vasut3dbab922020-03-15 15:36:09 +0100297 smc911x_reg_write(priv, RX_CFG, 0);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400298
Marek Vasut3dbab922020-03-15 15:36:09 +0100299 smc911x_set_mac_csr(priv, MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN |
Ben Warren736fead2009-07-20 22:01:11 -0700300 MAC_CR_HBDIS);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400301}
302
Marek Vasut8eb4fef2020-03-15 17:25:27 +0100303static int smc911x_init_common(struct smc911x_priv *priv)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400304{
Marek Vasut3dbab922020-03-15 15:36:09 +0100305 const struct chip_id *id = priv->chipid;
Sascha Hauerde1b6862008-04-15 00:08:20 -0400306
Wolfgang Denk49467752009-10-28 00:49:47 +0100307 printf(DRIVERNAME ": detected %s controller\n", id->name);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400308
Marek Vasut3dbab922020-03-15 15:36:09 +0100309 smc911x_reset(priv);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400310
311 /* Configure the PHY, initialize the link state */
Marek Vasut3dbab922020-03-15 15:36:09 +0100312 smc911x_phy_configure(priv);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400313
Marek Vasut3dbab922020-03-15 15:36:09 +0100314 smc911x_handle_mac_address(priv);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400315
316 /* Turn on Tx + Rx */
Marek Vasut3dbab922020-03-15 15:36:09 +0100317 smc911x_enable(priv);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400318
319 return 0;
Sascha Hauerde1b6862008-04-15 00:08:20 -0400320}
321
Marek Vasut8eb4fef2020-03-15 17:25:27 +0100322static int smc911x_send_common(struct smc911x_priv *priv,
323 void *packet, int length)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400324{
325 u32 *data = (u32*)packet;
326 u32 tmplen;
327 u32 status;
328
Marek Vasut3dbab922020-03-15 15:36:09 +0100329 smc911x_reg_write(priv, TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG |
Ben Warren736fead2009-07-20 22:01:11 -0700330 TX_CMD_A_INT_LAST_SEG | length);
Marek Vasut3dbab922020-03-15 15:36:09 +0100331 smc911x_reg_write(priv, TX_DATA_FIFO, length);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400332
333 tmplen = (length + 3) / 4;
334
Guennadi Liakhovetski3e0f3312008-04-29 12:35:08 +0000335 while (tmplen--)
Marek Vasut3dbab922020-03-15 15:36:09 +0100336 smc911x_reg_write(priv, TX_DATA_FIFO, *data++);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400337
338 /* wait for transmission */
Marek Vasut3dbab922020-03-15 15:36:09 +0100339 while (!((smc911x_reg_read(priv, TX_FIFO_INF) &
Ben Warren736fead2009-07-20 22:01:11 -0700340 TX_FIFO_INF_TSUSED) >> 16));
Sascha Hauerde1b6862008-04-15 00:08:20 -0400341
342 /* get status. Ignore 'no carrier' error, it has no meaning for
343 * full duplex operation
344 */
Marek Vasut3dbab922020-03-15 15:36:09 +0100345 status = smc911x_reg_read(priv, TX_STATUS_FIFO) &
Ben Warren736fead2009-07-20 22:01:11 -0700346 (TX_STS_LOC | TX_STS_LATE_COLL | TX_STS_MANY_COLL |
347 TX_STS_MANY_DEFER | TX_STS_UNDERRUN);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400348
Guennadi Liakhovetski3e0f3312008-04-29 12:35:08 +0000349 if (!status)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400350 return 0;
351
352 printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n",
353 status & TX_STS_LOC ? "TX_STS_LOC " : "",
354 status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "",
355 status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "",
356 status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "",
357 status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : "");
358
359 return -1;
360}
361
Marek Vasut8eb4fef2020-03-15 17:25:27 +0100362static void smc911x_halt_common(struct smc911x_priv *priv)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400363{
Marek Vasut3dbab922020-03-15 15:36:09 +0100364 smc911x_reset(priv);
365 smc911x_handle_mac_address(priv);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400366}
367
Marek Vasut8eb4fef2020-03-15 17:25:27 +0100368static int smc911x_recv_common(struct smc911x_priv *priv, u32 *data)
Sascha Hauerde1b6862008-04-15 00:08:20 -0400369{
Sascha Hauerde1b6862008-04-15 00:08:20 -0400370 u32 pktlen, tmplen;
371 u32 status;
372
Marek Vasutb11c8bb2020-03-15 17:02:05 +0100373 status = smc911x_reg_read(priv, RX_FIFO_INF);
374 if (!(status & RX_FIFO_INF_RXSUSED))
375 return 0;
Sascha Hauerde1b6862008-04-15 00:08:20 -0400376
Marek Vasutb11c8bb2020-03-15 17:02:05 +0100377 status = smc911x_reg_read(priv, RX_STATUS_FIFO);
378 pktlen = (status & RX_STS_PKT_LEN) >> 16;
Sascha Hauerde1b6862008-04-15 00:08:20 -0400379
Marek Vasutb11c8bb2020-03-15 17:02:05 +0100380 smc911x_reg_write(priv, RX_CFG, 0);
Sascha Hauerde1b6862008-04-15 00:08:20 -0400381
Marek Vasutb11c8bb2020-03-15 17:02:05 +0100382 tmplen = (pktlen + 3) / 4;
383 while (tmplen--)
384 *data++ = smc911x_reg_read(priv, RX_DATA_FIFO);
385
Marek Vasut8eb4fef2020-03-15 17:25:27 +0100386 if (status & RX_STS_ES) {
Marek Vasutb11c8bb2020-03-15 17:02:05 +0100387 printf(DRIVERNAME
388 ": dropped bad packet. Status: 0x%08x\n",
389 status);
Marek Vasut8eb4fef2020-03-15 17:25:27 +0100390 return 0;
391 }
Sascha Hauerde1b6862008-04-15 00:08:20 -0400392
Marek Vasut8eb4fef2020-03-15 17:25:27 +0100393 return pktlen;
Sascha Hauerde1b6862008-04-15 00:08:20 -0400394}
Ben Warren736fead2009-07-20 22:01:11 -0700395
Marek Vasut81486932020-03-15 17:39:01 +0100396#ifndef CONFIG_DM_ETH
397
Helmut Raiger6af1d412011-06-29 00:12:14 +0000398#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
399/* wrapper for smc911x_eth_phy_read */
Joe Hershberger5a49f172016-08-08 11:28:38 -0500400static int smc911x_miiphy_read(struct mii_dev *bus, int phy, int devad,
401 int reg)
Helmut Raiger6af1d412011-06-29 00:12:14 +0000402{
Joe Hershberger5a49f172016-08-08 11:28:38 -0500403 struct eth_device *dev = eth_get_dev_by_name(bus->name);
Marek Vasut3dbab922020-03-15 15:36:09 +0100404 struct smc911x_priv *priv = container_of(dev, struct smc911x_priv, dev);
Marek Vasut6f6cf002020-03-15 15:43:20 +0100405 u16 val = 0;
406 int ret;
407
Marek Vasut3dbab922020-03-15 15:36:09 +0100408 if (!dev || !priv)
Marek Vasut6f6cf002020-03-15 15:43:20 +0100409 return -ENODEV;
410
Marek Vasut3dbab922020-03-15 15:36:09 +0100411 ret = smc911x_eth_phy_read(priv, phy, reg, &val);
Marek Vasut6f6cf002020-03-15 15:43:20 +0100412 if (ret < 0)
413 return ret;
414
415 return val;
Helmut Raiger6af1d412011-06-29 00:12:14 +0000416}
Marek Vasut3dbab922020-03-15 15:36:09 +0100417
Helmut Raiger6af1d412011-06-29 00:12:14 +0000418/* wrapper for smc911x_eth_phy_write */
Joe Hershberger5a49f172016-08-08 11:28:38 -0500419static int smc911x_miiphy_write(struct mii_dev *bus, int phy, int devad,
420 int reg, u16 val)
Helmut Raiger6af1d412011-06-29 00:12:14 +0000421{
Joe Hershberger5a49f172016-08-08 11:28:38 -0500422 struct eth_device *dev = eth_get_dev_by_name(bus->name);
Marek Vasut3dbab922020-03-15 15:36:09 +0100423 struct smc911x_priv *priv = container_of(dev, struct smc911x_priv, dev);
Marek Vasut6f6cf002020-03-15 15:43:20 +0100424
Marek Vasut3dbab922020-03-15 15:36:09 +0100425 if (!dev || !priv)
Marek Vasut6f6cf002020-03-15 15:43:20 +0100426 return -ENODEV;
427
Marek Vasut3dbab922020-03-15 15:36:09 +0100428 return smc911x_eth_phy_write(priv, phy, reg, val);
Helmut Raiger6af1d412011-06-29 00:12:14 +0000429}
Marek Vasutf51a2f82020-03-21 17:25:41 +0100430
Marek Vasut3dbab922020-03-15 15:36:09 +0100431static int smc911x_initialize_mii(struct smc911x_priv *priv)
Marek Vasutf51a2f82020-03-21 17:25:41 +0100432{
433 struct mii_dev *mdiodev = mdio_alloc();
434 int ret;
435
436 if (!mdiodev)
437 return -ENOMEM;
438
Marek Vasut3dbab922020-03-15 15:36:09 +0100439 strncpy(mdiodev->name, priv->dev.name, MDIO_NAME_LEN);
Marek Vasutf51a2f82020-03-21 17:25:41 +0100440 mdiodev->read = smc911x_miiphy_read;
441 mdiodev->write = smc911x_miiphy_write;
442
443 ret = mdio_register(mdiodev);
444 if (ret < 0) {
445 mdio_free(mdiodev);
446 return ret;
447 }
448
449 return 0;
450}
451#else
Marek Vasut3dbab922020-03-15 15:36:09 +0100452static int smc911x_initialize_mii(struct smc911x_priv *priv)
Marek Vasutf51a2f82020-03-21 17:25:41 +0100453{
454 return 0;
455}
Helmut Raiger6af1d412011-06-29 00:12:14 +0000456#endif
457
Masahiro Yamadabb5a2cf2020-06-26 15:13:34 +0900458static int smc911x_init(struct eth_device *dev, struct bd_info *bd)
Marek Vasut8eb4fef2020-03-15 17:25:27 +0100459{
460 struct smc911x_priv *priv = container_of(dev, struct smc911x_priv, dev);
461
462 return smc911x_init_common(priv);
463}
464
465static void smc911x_halt(struct eth_device *dev)
466{
467 struct smc911x_priv *priv = container_of(dev, struct smc911x_priv, dev);
468
469 smc911x_halt_common(priv);
470}
471
472static int smc911x_send(struct eth_device *dev, void *packet, int length)
473{
474 struct smc911x_priv *priv = container_of(dev, struct smc911x_priv, dev);
475
476 return smc911x_send_common(priv, packet, length);
477}
478
479static int smc911x_recv(struct eth_device *dev)
480{
481 struct smc911x_priv *priv = container_of(dev, struct smc911x_priv, dev);
482 u32 *data = (u32 *)net_rx_packets[0];
483 int ret;
484
485 ret = smc911x_recv_common(priv, data);
486 if (ret)
487 net_process_received_packet(net_rx_packets[0], ret);
488
489 return ret;
490}
491
Ben Warren736fead2009-07-20 22:01:11 -0700492int smc911x_initialize(u8 dev_num, int base_addr)
493{
Marek Vasut3dbab922020-03-15 15:36:09 +0100494 struct smc911x_priv *priv;
Marek Vasutf51a2f82020-03-21 17:25:41 +0100495 int ret;
Ben Warren736fead2009-07-20 22:01:11 -0700496
Marek Vasut3dbab922020-03-15 15:36:09 +0100497 priv = calloc(1, sizeof(*priv));
498 if (!priv)
Marek Vasut882d5f62020-03-15 15:14:18 +0100499 return -ENOMEM;
Ben Warren736fead2009-07-20 22:01:11 -0700500
Marek Vasut3dbab922020-03-15 15:36:09 +0100501 priv->iobase = base_addr;
502 priv->dev.iobase = base_addr;
Ben Warren736fead2009-07-20 22:01:11 -0700503
Steve Sakoman4bc3d2a2009-10-20 18:21:18 +0200504 /* Try to detect chip. Will fail if not present. */
Marek Vasut3dbab922020-03-15 15:36:09 +0100505 ret = smc911x_detect_chip(priv);
Marek Vasutf51a2f82020-03-21 17:25:41 +0100506 if (ret) {
507 ret = 0; /* Card not detected is not an error */
508 goto err_detect;
Steve Sakoman4bc3d2a2009-10-20 18:21:18 +0200509 }
510
Andre Przywara117a52e2020-06-11 12:03:17 +0100511 if (smc911x_read_mac_address(priv))
Marek Vasut81486932020-03-15 17:39:01 +0100512 memcpy(priv->dev.enetaddr, priv->enetaddr, 6);
Ben Warren736fead2009-07-20 22:01:11 -0700513
Marek Vasut3dbab922020-03-15 15:36:09 +0100514 priv->dev.init = smc911x_init;
515 priv->dev.halt = smc911x_halt;
516 priv->dev.send = smc911x_send;
517 priv->dev.recv = smc911x_recv;
518 sprintf(priv->dev.name, "%s-%hu", DRIVERNAME, dev_num);
Ben Warren736fead2009-07-20 22:01:11 -0700519
Marek Vasut3dbab922020-03-15 15:36:09 +0100520 eth_register(&priv->dev);
Helmut Raiger6af1d412011-06-29 00:12:14 +0000521
Marek Vasut3dbab922020-03-15 15:36:09 +0100522 ret = smc911x_initialize_mii(priv);
Marek Vasutf51a2f82020-03-21 17:25:41 +0100523 if (ret)
524 goto err_mii;
Helmut Raiger6af1d412011-06-29 00:12:14 +0000525
Mike Rapoportfbd47b62009-11-12 15:35:08 +0200526 return 1;
Marek Vasutf51a2f82020-03-21 17:25:41 +0100527
528err_mii:
Marek Vasut3dbab922020-03-15 15:36:09 +0100529 eth_unregister(&priv->dev);
Marek Vasutf51a2f82020-03-21 17:25:41 +0100530err_detect:
Marek Vasut3dbab922020-03-15 15:36:09 +0100531 free(priv);
Marek Vasutf51a2f82020-03-21 17:25:41 +0100532 return ret;
Ben Warren736fead2009-07-20 22:01:11 -0700533}
Marek Vasut81486932020-03-15 17:39:01 +0100534
535#else /* ifdef CONFIG_DM_ETH */
536
537static int smc911x_start(struct udevice *dev)
538{
Simon Glassc69cda22020-12-03 16:55:20 -0700539 struct eth_pdata *plat = dev_get_plat(dev);
Marek Vasut81486932020-03-15 17:39:01 +0100540 struct smc911x_priv *priv = dev_get_priv(dev);
541
542 memcpy(priv->enetaddr, plat->enetaddr, sizeof(plat->enetaddr));
543
544 return smc911x_init_common(priv);
545}
546
547static void smc911x_stop(struct udevice *dev)
548{
549 struct smc911x_priv *priv = dev_get_priv(dev);
550
551 smc911x_halt_common(priv);
552}
553
554static int smc911x_send(struct udevice *dev, void *packet, int length)
555{
556 struct smc911x_priv *priv = dev_get_priv(dev);
557 int ret;
558
559 ret = smc911x_send_common(priv, packet, length);
560
561 return ret ? 0 : -ETIMEDOUT;
562}
563
564static int smc911x_recv(struct udevice *dev, int flags, uchar **packetp)
565{
566 struct smc911x_priv *priv = dev_get_priv(dev);
567 u32 *data = (u32 *)net_rx_packets[0];
568 int ret;
569
570 ret = smc911x_recv_common(priv, data);
571 if (ret)
572 *packetp = (void *)data;
573
574 return ret ? ret : -EAGAIN;
575}
576
Andre Przywara117a52e2020-06-11 12:03:17 +0100577static int smc911x_read_rom_hwaddr(struct udevice *dev)
578{
579 struct smc911x_priv *priv = dev_get_priv(dev);
Simon Glassc69cda22020-12-03 16:55:20 -0700580 struct eth_pdata *pdata = dev_get_plat(dev);
Andre Przywara117a52e2020-06-11 12:03:17 +0100581
582 if (!smc911x_read_mac_address(priv))
583 return -ENODEV;
584
585 memcpy(pdata->enetaddr, priv->enetaddr, sizeof(pdata->enetaddr));
586
587 return 0;
588}
589
Marek Vasut81486932020-03-15 17:39:01 +0100590static int smc911x_bind(struct udevice *dev)
591{
592 return device_set_name(dev, dev->name);
593}
594
595static int smc911x_probe(struct udevice *dev)
596{
597 struct smc911x_priv *priv = dev_get_priv(dev);
Marek Vasut81486932020-03-15 17:39:01 +0100598 int ret;
599
600 /* Try to detect chip. Will fail if not present. */
601 ret = smc911x_detect_chip(priv);
602 if (ret)
603 return ret;
604
Andre Przywara117a52e2020-06-11 12:03:17 +0100605 smc911x_read_rom_hwaddr(dev);
Marek Vasut81486932020-03-15 17:39:01 +0100606
607 return 0;
608}
609
Simon Glassd1998a92020-12-03 16:55:21 -0700610static int smc911x_of_to_plat(struct udevice *dev)
Marek Vasut81486932020-03-15 17:39:01 +0100611{
612 struct smc911x_priv *priv = dev_get_priv(dev);
Simon Glassc69cda22020-12-03 16:55:20 -0700613 struct eth_pdata *pdata = dev_get_plat(dev);
Marek Vasut81486932020-03-15 17:39:01 +0100614
Masahiro Yamada25484932020-07-17 14:36:48 +0900615 pdata->iobase = dev_read_addr(dev);
Marek Vasut81486932020-03-15 17:39:01 +0100616 priv->iobase = pdata->iobase;
617
618 return 0;
619}
620
621static const struct eth_ops smc911x_ops = {
622 .start = smc911x_start,
623 .send = smc911x_send,
624 .recv = smc911x_recv,
625 .stop = smc911x_stop,
Andre Przywara117a52e2020-06-11 12:03:17 +0100626 .read_rom_hwaddr = smc911x_read_rom_hwaddr,
Marek Vasut81486932020-03-15 17:39:01 +0100627};
628
629static const struct udevice_id smc911x_ids[] = {
630 { .compatible = "smsc,lan9115" },
631 { }
632};
633
634U_BOOT_DRIVER(smc911x) = {
635 .name = "eth_smc911x",
636 .id = UCLASS_ETH,
637 .of_match = smc911x_ids,
638 .bind = smc911x_bind,
Simon Glassd1998a92020-12-03 16:55:21 -0700639 .of_to_plat = smc911x_of_to_plat,
Marek Vasut81486932020-03-15 17:39:01 +0100640 .probe = smc911x_probe,
641 .ops = &smc911x_ops,
Simon Glass41575d82020-12-03 16:55:17 -0700642 .priv_auto = sizeof(struct smc911x_priv),
Simon Glasscaa4daa2020-12-03 16:55:18 -0700643 .plat_auto = sizeof(struct eth_pdata),
Marek Vasut81486932020-03-15 17:39:01 +0100644 .flags = DM_FLAG_ALLOC_PRIV_DMA,
645};
646#endif