]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c609719b WD |
2 | |
3 | #include <common.h> | |
02b95a4b | 4 | #include <asm/io.h> |
f23a785c | 5 | #include <dm.h> |
c609719b WD |
6 | #include <malloc.h> |
7 | #include <net.h> | |
8ca0b3f9 | 8 | #include <netdev.h> |
c609719b | 9 | #include <pci.h> |
cd93d625 | 10 | #include <linux/bitops.h> |
c05ed00a | 11 | #include <linux/delay.h> |
c609719b | 12 | |
c2abfca9 | 13 | #define SROM_DLEVEL 0 |
c609719b | 14 | |
eb216f1e MV |
15 | /* PCI Registers. */ |
16 | #define PCI_CFDA_PSM 0x43 | |
c609719b WD |
17 | |
18 | #define CFRV_RN 0x000000f0 /* Revision Number */ | |
19 | ||
20 | #define WAKEUP 0x00 /* Power Saving Wakeup */ | |
21 | #define SLEEP 0x80 /* Power Saving Sleep Mode */ | |
22 | ||
eb216f1e | 23 | #define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */ |
c609719b | 24 | |
eb216f1e | 25 | /* Ethernet chip registers. */ |
c609719b WD |
26 | #define DE4X5_BMR 0x000 /* Bus Mode Register */ |
27 | #define DE4X5_TPD 0x008 /* Transmit Poll Demand Reg */ | |
28 | #define DE4X5_RRBA 0x018 /* RX Ring Base Address Reg */ | |
29 | #define DE4X5_TRBA 0x020 /* TX Ring Base Address Reg */ | |
30 | #define DE4X5_STS 0x028 /* Status Register */ | |
31 | #define DE4X5_OMR 0x030 /* Operation Mode Register */ | |
32 | #define DE4X5_SICR 0x068 /* SIA Connectivity Register */ | |
33 | #define DE4X5_APROM 0x048 /* Ethernet Address PROM */ | |
34 | ||
eb216f1e | 35 | /* Register bits. */ |
c609719b WD |
36 | #define BMR_SWR 0x00000001 /* Software Reset */ |
37 | #define STS_TS 0x00700000 /* Transmit Process State */ | |
38 | #define STS_RS 0x000e0000 /* Receive Process State */ | |
39 | #define OMR_ST 0x00002000 /* Start/Stop Transmission Command */ | |
40 | #define OMR_SR 0x00000002 /* Start/Stop Receive */ | |
41 | #define OMR_PS 0x00040000 /* Port Select */ | |
42 | #define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */ | |
43 | #define OMR_PM 0x00000080 /* Pass All Multicast */ | |
44 | ||
eb216f1e | 45 | /* Descriptor bits. */ |
c609719b WD |
46 | #define R_OWN 0x80000000 /* Own Bit */ |
47 | #define RD_RER 0x02000000 /* Receive End Of Ring */ | |
48 | #define RD_LS 0x00000100 /* Last Descriptor */ | |
49 | #define RD_ES 0x00008000 /* Error Summary */ | |
50 | #define TD_TER 0x02000000 /* Transmit End Of Ring */ | |
51 | #define T_OWN 0x80000000 /* Own Bit */ | |
52 | #define TD_LS 0x40000000 /* Last Segment */ | |
53 | #define TD_FS 0x20000000 /* First Segment */ | |
54 | #define TD_ES 0x00008000 /* Error Summary */ | |
55 | #define TD_SET 0x08000000 /* Setup Packet */ | |
56 | ||
57 | /* The EEPROM commands include the alway-set leading bit. */ | |
58 | #define SROM_WRITE_CMD 5 | |
59 | #define SROM_READ_CMD 6 | |
60 | #define SROM_ERASE_CMD 7 | |
61 | ||
eb216f1e | 62 | #define SROM_HWADD 0x0014 /* Hardware Address offset in SROM */ |
c609719b | 63 | #define SROM_RD 0x00004000 /* Read from Boot ROM */ |
eb216f1e MV |
64 | #define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */ |
65 | #define EE_WRITE_0 0x4801 | |
66 | #define EE_WRITE_1 0x4805 | |
67 | #define EE_DATA_READ 0x08 /* EEPROM chip data out. */ | |
c609719b WD |
68 | #define SROM_SR 0x00000800 /* Select Serial ROM when set */ |
69 | ||
70 | #define DT_IN 0x00000004 /* Serial Data In */ | |
71 | #define DT_CLK 0x00000002 /* Serial ROM Clock */ | |
72 | #define DT_CS 0x00000001 /* Serial ROM Chip Select */ | |
73 | ||
74 | #define POLL_DEMAND 1 | |
75 | ||
f23a785c MV |
76 | #if defined(CONFIG_DM_ETH) |
77 | #define phys_to_bus(dev, a) dm_pci_phys_to_mem((dev), (a)) | |
78 | #elif defined(CONFIG_E500) | |
8a5c6f15 | 79 | #define phys_to_bus(dev, a) (a) |
04da0612 | 80 | #else |
8a5c6f15 | 81 | #define phys_to_bus(dev, a) pci_phys_to_mem((dev), (a)) |
04da0612 MV |
82 | #endif |
83 | ||
dbe9c0c1 MV |
84 | #define NUM_RX_DESC PKTBUFSRX |
85 | #define NUM_TX_DESC 1 /* Number of TX descriptors */ | |
86 | #define RX_BUFF_SZ PKTSIZE_ALIGN | |
87 | ||
88 | #define TOUT_LOOP 1000000 | |
89 | ||
90 | #define SETUP_FRAME_LEN 192 | |
91 | ||
92 | struct de4x5_desc { | |
93 | volatile s32 status; | |
94 | u32 des1; | |
95 | u32 buf; | |
96 | u32 next; | |
97 | }; | |
98 | ||
2301a4be | 99 | struct dc2114x_priv { |
32d8d118 MV |
100 | struct de4x5_desc rx_ring[NUM_RX_DESC] __aligned(32); |
101 | struct de4x5_desc tx_ring[NUM_TX_DESC] __aligned(32); | |
102 | int rx_new; /* RX descriptor ring pointer */ | |
103 | int tx_new; /* TX descriptor ring pointer */ | |
104 | char rx_ring_size; | |
105 | char tx_ring_size; | |
f23a785c MV |
106 | #ifdef CONFIG_DM_ETH |
107 | struct udevice *devno; | |
108 | #else | |
2301a4be | 109 | struct eth_device dev; |
8a5c6f15 | 110 | pci_dev_t devno; |
f23a785c | 111 | #endif |
2301a4be MV |
112 | char *name; |
113 | void __iomem *iobase; | |
114 | u8 *enetaddr; | |
115 | }; | |
116 | ||
dbe9c0c1 | 117 | /* RX and TX descriptor ring */ |
fcd62178 | 118 | static u32 dc2114x_inl(struct dc2114x_priv *priv, u32 addr) |
04da0612 | 119 | { |
fcd62178 | 120 | return le32_to_cpu(readl(priv->iobase + addr)); |
04da0612 MV |
121 | } |
122 | ||
fcd62178 | 123 | static void dc2114x_outl(struct dc2114x_priv *priv, u32 command, u32 addr) |
04da0612 | 124 | { |
fcd62178 | 125 | writel(cpu_to_le32(command), priv->iobase + addr); |
04da0612 MV |
126 | } |
127 | ||
fcd62178 | 128 | static void reset_de4x5(struct dc2114x_priv *priv) |
04da0612 | 129 | { |
3b7b9e2e | 130 | u32 i; |
04da0612 | 131 | |
fcd62178 | 132 | i = dc2114x_inl(priv, DE4X5_BMR); |
04da0612 | 133 | mdelay(1); |
fcd62178 | 134 | dc2114x_outl(priv, i | BMR_SWR, DE4X5_BMR); |
04da0612 | 135 | mdelay(1); |
fcd62178 | 136 | dc2114x_outl(priv, i, DE4X5_BMR); |
04da0612 MV |
137 | mdelay(1); |
138 | ||
139 | for (i = 0; i < 5; i++) { | |
fcd62178 | 140 | dc2114x_inl(priv, DE4X5_BMR); |
04da0612 MV |
141 | mdelay(10); |
142 | } | |
143 | ||
144 | mdelay(1); | |
c609719b WD |
145 | } |
146 | ||
fcd62178 | 147 | static void start_de4x5(struct dc2114x_priv *priv) |
04da0612 | 148 | { |
3b7b9e2e | 149 | u32 omr; |
04da0612 | 150 | |
fcd62178 | 151 | omr = dc2114x_inl(priv, DE4X5_OMR); |
04da0612 | 152 | omr |= OMR_ST | OMR_SR; |
fcd62178 | 153 | dc2114x_outl(priv, omr, DE4X5_OMR); /* Enable the TX and/or RX */ |
c609719b WD |
154 | } |
155 | ||
fcd62178 | 156 | static void stop_de4x5(struct dc2114x_priv *priv) |
04da0612 | 157 | { |
3b7b9e2e | 158 | u32 omr; |
04da0612 | 159 | |
fcd62178 | 160 | omr = dc2114x_inl(priv, DE4X5_OMR); |
04da0612 | 161 | omr &= ~(OMR_ST | OMR_SR); |
fcd62178 | 162 | dc2114x_outl(priv, omr, DE4X5_OMR); /* Disable the TX and/or RX */ |
c609719b WD |
163 | } |
164 | ||
dbe9c0c1 | 165 | /* SROM Read and write routines. */ |
fcd62178 | 166 | static void sendto_srom(struct dc2114x_priv *priv, u_int command, u_long addr) |
dbe9c0c1 | 167 | { |
fcd62178 | 168 | dc2114x_outl(priv, command, addr); |
dbe9c0c1 MV |
169 | udelay(1); |
170 | } | |
c609719b | 171 | |
fcd62178 | 172 | static int getfrom_srom(struct dc2114x_priv *priv, u_long addr) |
dbe9c0c1 | 173 | { |
fcd62178 | 174 | u32 tmp = dc2114x_inl(priv, addr); |
c609719b | 175 | |
dbe9c0c1 MV |
176 | udelay(1); |
177 | return tmp; | |
178 | } | |
c609719b | 179 | |
dbe9c0c1 | 180 | /* Note: this routine returns extra data bits for size detection. */ |
fcd62178 | 181 | static int do_read_eeprom(struct dc2114x_priv *priv, u_long ioaddr, int location, |
dbe9c0c1 MV |
182 | int addr_len) |
183 | { | |
184 | int read_cmd = location | (SROM_READ_CMD << addr_len); | |
185 | unsigned int retval = 0; | |
186 | int i; | |
eb216f1e | 187 | |
fcd62178 MV |
188 | sendto_srom(priv, SROM_RD | SROM_SR, ioaddr); |
189 | sendto_srom(priv, SROM_RD | SROM_SR | DT_CS, ioaddr); | |
eb216f1e | 190 | |
c2abfca9 | 191 | debug_cond(SROM_DLEVEL >= 1, " EEPROM read at %d ", location); |
c609719b | 192 | |
dbe9c0c1 MV |
193 | /* Shift the read command bits out. */ |
194 | for (i = 4 + addr_len; i >= 0; i--) { | |
195 | short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; | |
c609719b | 196 | |
fcd62178 | 197 | sendto_srom(priv, SROM_RD | SROM_SR | DT_CS | dataval, |
dbe9c0c1 MV |
198 | ioaddr); |
199 | udelay(10); | |
fcd62178 | 200 | sendto_srom(priv, SROM_RD | SROM_SR | DT_CS | dataval | DT_CLK, |
dbe9c0c1 MV |
201 | ioaddr); |
202 | udelay(10); | |
c2abfca9 | 203 | debug_cond(SROM_DLEVEL >= 2, "%X", |
fcd62178 | 204 | getfrom_srom(priv, ioaddr) & 15); |
dbe9c0c1 | 205 | retval = (retval << 1) | |
fcd62178 | 206 | !!(getfrom_srom(priv, ioaddr) & EE_DATA_READ); |
dbe9c0c1 | 207 | } |
c609719b | 208 | |
fcd62178 | 209 | sendto_srom(priv, SROM_RD | SROM_SR | DT_CS, ioaddr); |
ca5cb04b | 210 | |
fcd62178 | 211 | debug_cond(SROM_DLEVEL >= 2, " :%X:", getfrom_srom(priv, ioaddr) & 15); |
c609719b | 212 | |
dbe9c0c1 | 213 | for (i = 16; i > 0; i--) { |
fcd62178 | 214 | sendto_srom(priv, SROM_RD | SROM_SR | DT_CS | DT_CLK, ioaddr); |
dbe9c0c1 | 215 | udelay(10); |
c2abfca9 | 216 | debug_cond(SROM_DLEVEL >= 2, "%X", |
fcd62178 | 217 | getfrom_srom(priv, ioaddr) & 15); |
dbe9c0c1 | 218 | retval = (retval << 1) | |
fcd62178 MV |
219 | !!(getfrom_srom(priv, ioaddr) & EE_DATA_READ); |
220 | sendto_srom(priv, SROM_RD | SROM_SR | DT_CS, ioaddr); | |
dbe9c0c1 MV |
221 | udelay(10); |
222 | } | |
c609719b | 223 | |
dbe9c0c1 | 224 | /* Terminate the EEPROM access. */ |
fcd62178 | 225 | sendto_srom(priv, SROM_RD | SROM_SR, ioaddr); |
c609719b | 226 | |
c2abfca9 MV |
227 | debug_cond(SROM_DLEVEL >= 2, " EEPROM value at %d is %5.5x.\n", |
228 | location, retval); | |
c609719b | 229 | |
dbe9c0c1 MV |
230 | return retval; |
231 | } | |
c609719b | 232 | |
dbe9c0c1 MV |
233 | /* |
234 | * This executes a generic EEPROM command, typically a write or write | |
235 | * enable. It returns the data output from the EEPROM, and thus may | |
236 | * also be used for reads. | |
237 | */ | |
fcd62178 | 238 | static int do_eeprom_cmd(struct dc2114x_priv *priv, u_long ioaddr, int cmd, |
dbe9c0c1 MV |
239 | int cmd_len) |
240 | { | |
241 | unsigned int retval = 0; | |
c609719b | 242 | |
c2abfca9 | 243 | debug_cond(SROM_DLEVEL >= 1, " EEPROM op 0x%x: ", cmd); |
c609719b | 244 | |
fcd62178 | 245 | sendto_srom(priv, SROM_RD | SROM_SR | DT_CS | DT_CLK, ioaddr); |
c609719b | 246 | |
dbe9c0c1 MV |
247 | /* Shift the command bits out. */ |
248 | do { | |
249 | short dataval = (cmd & BIT(cmd_len)) ? EE_WRITE_1 : EE_WRITE_0; | |
c609719b | 250 | |
fcd62178 | 251 | sendto_srom(priv, dataval, ioaddr); |
dbe9c0c1 | 252 | udelay(10); |
ca5cb04b | 253 | |
c2abfca9 | 254 | debug_cond(SROM_DLEVEL >= 2, "%X", |
fcd62178 | 255 | getfrom_srom(priv, ioaddr) & 15); |
be44f758 | 256 | |
fcd62178 | 257 | sendto_srom(priv, dataval | DT_CLK, ioaddr); |
dbe9c0c1 MV |
258 | udelay(10); |
259 | retval = (retval << 1) | | |
fcd62178 | 260 | !!(getfrom_srom(priv, ioaddr) & EE_DATA_READ); |
dbe9c0c1 | 261 | } while (--cmd_len >= 0); |
63f34912 | 262 | |
fcd62178 | 263 | sendto_srom(priv, SROM_RD | SROM_SR | DT_CS, ioaddr); |
c609719b | 264 | |
dbe9c0c1 | 265 | /* Terminate the EEPROM access. */ |
fcd62178 | 266 | sendto_srom(priv, SROM_RD | SROM_SR, ioaddr); |
c609719b | 267 | |
c2abfca9 | 268 | debug_cond(SROM_DLEVEL >= 1, " EEPROM result is 0x%5.5x.\n", retval); |
c609719b | 269 | |
dbe9c0c1 MV |
270 | return retval; |
271 | } | |
171f5e58 | 272 | |
fcd62178 | 273 | static int read_srom(struct dc2114x_priv *priv, u_long ioaddr, int index) |
dbe9c0c1 MV |
274 | { |
275 | int ee_addr_size; | |
c609719b | 276 | |
fcd62178 | 277 | ee_addr_size = (do_read_eeprom(priv, ioaddr, 0xff, 8) & BIT(18)) ? 8 : 6; |
c609719b | 278 | |
fcd62178 | 279 | return do_eeprom_cmd(priv, ioaddr, 0xffff | |
dbe9c0c1 MV |
280 | (((SROM_READ_CMD << ee_addr_size) | index) << 16), |
281 | 3 + ee_addr_size + 16); | |
c609719b WD |
282 | } |
283 | ||
bc4666ac | 284 | static void send_setup_frame(struct dc2114x_priv *priv) |
dbe9c0c1 MV |
285 | { |
286 | char setup_frame[SETUP_FRAME_LEN]; | |
287 | char *pa = &setup_frame[0]; | |
288 | int i; | |
289 | ||
290 | memset(pa, 0xff, SETUP_FRAME_LEN); | |
291 | ||
292 | for (i = 0; i < ETH_ALEN; i++) { | |
fcd62178 | 293 | *(pa + (i & 1)) = priv->enetaddr[i]; |
dbe9c0c1 MV |
294 | if (i & 0x01) |
295 | pa += 4; | |
c609719b WD |
296 | } |
297 | ||
32d8d118 | 298 | for (i = 0; priv->tx_ring[priv->tx_new].status & cpu_to_le32(T_OWN); i++) { |
dbe9c0c1 MV |
299 | if (i < TOUT_LOOP) |
300 | continue; | |
c609719b | 301 | |
fcd62178 | 302 | printf("%s: tx error buffer not ready\n", priv->name); |
dbe9c0c1 MV |
303 | return; |
304 | } | |
c609719b | 305 | |
32d8d118 | 306 | priv->tx_ring[priv->tx_new].buf = cpu_to_le32(phys_to_bus(priv->devno, |
8a5c6f15 | 307 | (u32)&setup_frame[0])); |
32d8d118 MV |
308 | priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_TER | TD_SET | SETUP_FRAME_LEN); |
309 | priv->tx_ring[priv->tx_new].status = cpu_to_le32(T_OWN); | |
c609719b | 310 | |
fcd62178 | 311 | dc2114x_outl(priv, POLL_DEMAND, DE4X5_TPD); |
c609719b | 312 | |
32d8d118 | 313 | for (i = 0; priv->tx_ring[priv->tx_new].status & cpu_to_le32(T_OWN); i++) { |
dbe9c0c1 MV |
314 | if (i < TOUT_LOOP) |
315 | continue; | |
c609719b | 316 | |
fcd62178 | 317 | printf("%s: tx buffer not ready\n", priv->name); |
dbe9c0c1 MV |
318 | return; |
319 | } | |
c609719b | 320 | |
32d8d118 | 321 | if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) != 0x7FFFFFFF) { |
dbe9c0c1 | 322 | printf("TX error status2 = 0x%08X\n", |
32d8d118 | 323 | le32_to_cpu(priv->tx_ring[priv->tx_new].status)); |
dbe9c0c1 MV |
324 | } |
325 | ||
32d8d118 | 326 | priv->tx_new = (priv->tx_new + 1) % NUM_TX_DESC; |
c609719b WD |
327 | } |
328 | ||
bc4666ac | 329 | static int dc21x4x_send_common(struct dc2114x_priv *priv, void *packet, int length) |
c609719b | 330 | { |
7c53e336 MV |
331 | int status = -1; |
332 | int i; | |
c609719b WD |
333 | |
334 | if (length <= 0) { | |
fcd62178 | 335 | printf("%s: bad packet size: %d\n", priv->name, length); |
7c53e336 | 336 | goto done; |
c609719b WD |
337 | } |
338 | ||
32d8d118 | 339 | for (i = 0; priv->tx_ring[priv->tx_new].status & cpu_to_le32(T_OWN); i++) { |
7c53e336 MV |
340 | if (i < TOUT_LOOP) |
341 | continue; | |
342 | ||
fcd62178 | 343 | printf("%s: tx error buffer not ready\n", priv->name); |
7c53e336 | 344 | goto done; |
c609719b WD |
345 | } |
346 | ||
32d8d118 | 347 | priv->tx_ring[priv->tx_new].buf = cpu_to_le32(phys_to_bus(priv->devno, |
8a5c6f15 | 348 | (u32)packet)); |
32d8d118 MV |
349 | priv->tx_ring[priv->tx_new].des1 = cpu_to_le32(TD_TER | TD_LS | TD_FS | length); |
350 | priv->tx_ring[priv->tx_new].status = cpu_to_le32(T_OWN); | |
c609719b | 351 | |
fcd62178 | 352 | dc2114x_outl(priv, POLL_DEMAND, DE4X5_TPD); |
c609719b | 353 | |
32d8d118 | 354 | for (i = 0; priv->tx_ring[priv->tx_new].status & cpu_to_le32(T_OWN); i++) { |
7c53e336 MV |
355 | if (i < TOUT_LOOP) |
356 | continue; | |
357 | ||
fcd62178 | 358 | printf(".%s: tx buffer not ready\n", priv->name); |
7c53e336 | 359 | goto done; |
c609719b WD |
360 | } |
361 | ||
32d8d118 MV |
362 | if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) & TD_ES) { |
363 | priv->tx_ring[priv->tx_new].status = 0x0; | |
7c53e336 | 364 | goto done; |
c609719b WD |
365 | } |
366 | ||
367 | status = length; | |
368 | ||
7c53e336 | 369 | done: |
32d8d118 | 370 | priv->tx_new = (priv->tx_new + 1) % NUM_TX_DESC; |
c609719b WD |
371 | return status; |
372 | } | |
373 | ||
05c49179 MV |
374 | static int dc21x4x_recv_check(struct dc2114x_priv *priv) |
375 | { | |
376 | int length = 0; | |
377 | u32 status; | |
378 | ||
379 | status = le32_to_cpu(priv->rx_ring[priv->rx_new].status); | |
380 | ||
381 | if (status & R_OWN) | |
382 | return 0; | |
383 | ||
384 | if (status & RD_LS) { | |
385 | /* Valid frame status. */ | |
386 | if (status & RD_ES) { | |
387 | /* There was an error. */ | |
388 | printf("RX error status = 0x%08X\n", status); | |
389 | return -EINVAL; | |
390 | } else { | |
391 | /* A valid frame received. */ | |
392 | length = (le32_to_cpu(priv->rx_ring[priv->rx_new].status) | |
393 | >> 16); | |
394 | ||
395 | return length; | |
396 | } | |
397 | } | |
398 | ||
399 | return -EAGAIN; | |
400 | } | |
401 | ||
bc4666ac | 402 | static int dc21x4x_init_common(struct dc2114x_priv *priv) |
c609719b | 403 | { |
fcd62178 | 404 | int i; |
c609719b | 405 | |
fcd62178 | 406 | reset_de4x5(priv); |
c609719b | 407 | |
fcd62178 | 408 | if (dc2114x_inl(priv, DE4X5_STS) & (STS_TS | STS_RS)) { |
dbe9c0c1 MV |
409 | printf("Error: Cannot reset ethernet controller.\n"); |
410 | return -1; | |
411 | } | |
c609719b | 412 | |
fcd62178 | 413 | dc2114x_outl(priv, OMR_SDP | OMR_PS | OMR_PM, DE4X5_OMR); |
c609719b | 414 | |
dbe9c0c1 | 415 | for (i = 0; i < NUM_RX_DESC; i++) { |
32d8d118 MV |
416 | priv->rx_ring[i].status = cpu_to_le32(R_OWN); |
417 | priv->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); | |
418 | priv->rx_ring[i].buf = cpu_to_le32(phys_to_bus(priv->devno, | |
8a5c6f15 | 419 | (u32)net_rx_packets[i])); |
32d8d118 | 420 | priv->rx_ring[i].next = 0; |
c609719b WD |
421 | } |
422 | ||
dbe9c0c1 | 423 | for (i = 0; i < NUM_TX_DESC; i++) { |
32d8d118 MV |
424 | priv->tx_ring[i].status = 0; |
425 | priv->tx_ring[i].des1 = 0; | |
426 | priv->tx_ring[i].buf = 0; | |
427 | priv->tx_ring[i].next = 0; | |
c609719b WD |
428 | } |
429 | ||
32d8d118 MV |
430 | priv->rx_ring_size = NUM_RX_DESC; |
431 | priv->tx_ring_size = NUM_TX_DESC; | |
c609719b | 432 | |
dbe9c0c1 | 433 | /* Write the end of list marker to the descriptor lists. */ |
32d8d118 MV |
434 | priv->rx_ring[priv->rx_ring_size - 1].des1 |= cpu_to_le32(RD_RER); |
435 | priv->tx_ring[priv->tx_ring_size - 1].des1 |= cpu_to_le32(TD_TER); | |
c609719b | 436 | |
dbe9c0c1 | 437 | /* Tell the adapter where the TX/RX rings are located. */ |
32d8d118 | 438 | dc2114x_outl(priv, phys_to_bus(priv->devno, (u32)&priv->rx_ring), |
8a5c6f15 | 439 | DE4X5_RRBA); |
32d8d118 | 440 | dc2114x_outl(priv, phys_to_bus(priv->devno, (u32)&priv->tx_ring), |
8a5c6f15 | 441 | DE4X5_TRBA); |
5a0c332a | 442 | |
fcd62178 | 443 | start_de4x5(priv); |
c609719b | 444 | |
32d8d118 MV |
445 | priv->tx_new = 0; |
446 | priv->rx_new = 0; | |
63f34912 | 447 | |
bc4666ac | 448 | send_setup_frame(priv); |
c609719b | 449 | |
dbe9c0c1 | 450 | return 0; |
c609719b WD |
451 | } |
452 | ||
bc4666ac | 453 | static void dc21x4x_halt_common(struct dc2114x_priv *priv) |
c609719b | 454 | { |
fcd62178 MV |
455 | stop_de4x5(priv); |
456 | dc2114x_outl(priv, 0, DE4X5_SICR); | |
c609719b WD |
457 | } |
458 | ||
2301a4be | 459 | static void read_hw_addr(struct dc2114x_priv *priv) |
c609719b | 460 | { |
2301a4be | 461 | u_short tmp, *p = (u_short *)(&priv->enetaddr[0]); |
dbe9c0c1 | 462 | int i, j = 0; |
2e5c2a10 | 463 | |
dbe9c0c1 | 464 | for (i = 0; i < (ETH_ALEN >> 1); i++) { |
fcd62178 | 465 | tmp = read_srom(priv, DE4X5_APROM, (SROM_HWADD >> 1) + i); |
dbe9c0c1 MV |
466 | *p = le16_to_cpu(tmp); |
467 | j += *p++; | |
c609719b WD |
468 | } |
469 | ||
dbe9c0c1 | 470 | if (!j || j == 0x2fffd) { |
2301a4be | 471 | memset(priv->enetaddr, 0, ETH_ALEN); |
dbe9c0c1 | 472 | debug("Warning: can't read HW address from SROM.\n"); |
c609719b | 473 | } |
c609719b WD |
474 | } |
475 | ||
dbe9c0c1 | 476 | static struct pci_device_id supported[] = { |
75e375b0 MV |
477 | { PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST) }, |
478 | { PCI_DEVICE(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142) }, | |
dbe9c0c1 MV |
479 | { } |
480 | }; | |
c609719b | 481 | |
f23a785c | 482 | #ifndef CONFIG_DM_ETH |
bc4666ac MV |
483 | static int dc21x4x_init(struct eth_device *dev, struct bd_info *bis) |
484 | { | |
485 | struct dc2114x_priv *priv = | |
486 | container_of(dev, struct dc2114x_priv, dev); | |
487 | ||
488 | /* Ensure we're not sleeping. */ | |
489 | pci_write_config_byte(priv->devno, PCI_CFDA_PSM, WAKEUP); | |
490 | ||
491 | return dc21x4x_init_common(priv); | |
492 | } | |
493 | ||
494 | static void dc21x4x_halt(struct eth_device *dev) | |
495 | { | |
496 | struct dc2114x_priv *priv = | |
497 | container_of(dev, struct dc2114x_priv, dev); | |
498 | ||
499 | dc21x4x_halt_common(priv); | |
500 | ||
501 | pci_write_config_byte(priv->devno, PCI_CFDA_PSM, SLEEP); | |
502 | } | |
503 | ||
504 | static int dc21x4x_send(struct eth_device *dev, void *packet, int length) | |
505 | { | |
506 | struct dc2114x_priv *priv = | |
507 | container_of(dev, struct dc2114x_priv, dev); | |
508 | ||
509 | return dc21x4x_send_common(priv, packet, length); | |
510 | } | |
511 | ||
512 | static int dc21x4x_recv(struct eth_device *dev) | |
513 | { | |
514 | struct dc2114x_priv *priv = | |
515 | container_of(dev, struct dc2114x_priv, dev); | |
516 | int length = 0; | |
517 | int ret; | |
518 | ||
519 | while (true) { | |
520 | ret = dc21x4x_recv_check(priv); | |
521 | if (!ret) | |
522 | break; | |
523 | ||
524 | if (ret > 0) { | |
525 | length = ret; | |
526 | /* Pass the packet up to the protocol layers */ | |
527 | net_process_received_packet | |
528 | (net_rx_packets[priv->rx_new], length - 4); | |
529 | } | |
530 | ||
531 | /* | |
532 | * Change buffer ownership for this frame, | |
533 | * back to the adapter. | |
534 | */ | |
535 | if (ret != -EAGAIN) | |
536 | priv->rx_ring[priv->rx_new].status = cpu_to_le32(R_OWN); | |
537 | ||
538 | /* Update entry information. */ | |
539 | priv->rx_new = (priv->rx_new + 1) % priv->rx_ring_size; | |
540 | } | |
541 | ||
542 | return length; | |
543 | } | |
544 | ||
b75d8dc5 | 545 | int dc21x4x_initialize(struct bd_info *bis) |
c609719b | 546 | { |
2301a4be | 547 | struct dc2114x_priv *priv; |
dbe9c0c1 MV |
548 | struct eth_device *dev; |
549 | unsigned short status; | |
550 | unsigned char timer; | |
551 | unsigned int iobase; | |
552 | int card_number = 0; | |
553 | pci_dev_t devbusfn; | |
dbe9c0c1 | 554 | int idx = 0; |
2e5c2a10 | 555 | |
dbe9c0c1 MV |
556 | while (1) { |
557 | devbusfn = pci_find_devices(supported, idx++); | |
558 | if (devbusfn == -1) | |
559 | break; | |
c609719b | 560 | |
dbe9c0c1 MV |
561 | pci_read_config_word(devbusfn, PCI_COMMAND, &status); |
562 | status |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | |
563 | pci_write_config_word(devbusfn, PCI_COMMAND, status); | |
c609719b | 564 | |
dbe9c0c1 MV |
565 | pci_read_config_word(devbusfn, PCI_COMMAND, &status); |
566 | if (!(status & PCI_COMMAND_MEMORY)) { | |
567 | printf("Error: Can not enable MEMORY access.\n"); | |
568 | continue; | |
569 | } | |
c609719b | 570 | |
dbe9c0c1 MV |
571 | if (!(status & PCI_COMMAND_MASTER)) { |
572 | printf("Error: Can not enable Bus Mastering.\n"); | |
573 | continue; | |
574 | } | |
c609719b | 575 | |
dbe9c0c1 MV |
576 | /* Check the latency timer for values >= 0x60. */ |
577 | pci_read_config_byte(devbusfn, PCI_LATENCY_TIMER, &timer); | |
c609719b | 578 | |
dbe9c0c1 MV |
579 | if (timer < 0x60) { |
580 | pci_write_config_byte(devbusfn, PCI_LATENCY_TIMER, | |
581 | 0x60); | |
582 | } | |
c609719b | 583 | |
dbe9c0c1 MV |
584 | /* read BAR for memory space access */ |
585 | pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_1, &iobase); | |
586 | iobase &= PCI_BASE_ADDRESS_MEM_MASK; | |
587 | debug("dc21x4x: DEC 21142 PCI Device @0x%x\n", iobase); | |
c609719b | 588 | |
32d8d118 | 589 | priv = memalign(32, sizeof(*priv)); |
2301a4be | 590 | if (!priv) { |
dbe9c0c1 MV |
591 | printf("Can not allocalte memory of dc21x4x\n"); |
592 | break; | |
593 | } | |
2301a4be | 594 | memset(priv, 0, sizeof(*priv)); |
c609719b | 595 | |
2301a4be | 596 | dev = &priv->dev; |
2e5c2a10 | 597 | |
dbe9c0c1 | 598 | sprintf(dev->name, "dc21x4x#%d", card_number); |
8a5c6f15 | 599 | priv->devno = devbusfn; |
2301a4be MV |
600 | priv->name = dev->name; |
601 | priv->enetaddr = dev->enetaddr; | |
c609719b | 602 | |
dbe9c0c1 MV |
603 | dev->iobase = pci_mem_to_phys(devbusfn, iobase); |
604 | dev->priv = (void *)devbusfn; | |
605 | dev->init = dc21x4x_init; | |
606 | dev->halt = dc21x4x_halt; | |
607 | dev->send = dc21x4x_send; | |
608 | dev->recv = dc21x4x_recv; | |
c609719b | 609 | |
dbe9c0c1 MV |
610 | /* Ensure we're not sleeping. */ |
611 | pci_write_config_byte(devbusfn, PCI_CFDA_PSM, WAKEUP); | |
c609719b | 612 | |
dbe9c0c1 | 613 | udelay(10 * 1000); |
c609719b | 614 | |
2301a4be | 615 | read_hw_addr(priv); |
c609719b | 616 | |
dbe9c0c1 | 617 | eth_register(dev); |
2e5c2a10 | 618 | |
dbe9c0c1 MV |
619 | card_number++; |
620 | } | |
c609719b | 621 | |
dbe9c0c1 | 622 | return card_number; |
c609719b | 623 | } |
f23a785c MV |
624 | |
625 | #else /* DM_ETH */ | |
626 | static int dc2114x_start(struct udevice *dev) | |
627 | { | |
628 | struct eth_pdata *plat = dev_get_platdata(dev); | |
629 | struct dc2114x_priv *priv = dev_get_priv(dev); | |
630 | ||
631 | memcpy(priv->enetaddr, plat->enetaddr, sizeof(plat->enetaddr)); | |
632 | ||
633 | /* Ensure we're not sleeping. */ | |
634 | dm_pci_write_config8(dev, PCI_CFDA_PSM, WAKEUP); | |
635 | ||
636 | return dc21x4x_init_common(priv); | |
637 | } | |
638 | ||
639 | static void dc2114x_stop(struct udevice *dev) | |
640 | { | |
641 | struct dc2114x_priv *priv = dev_get_priv(dev); | |
642 | ||
643 | dc21x4x_halt_common(priv); | |
644 | ||
645 | dm_pci_write_config8(dev, PCI_CFDA_PSM, SLEEP); | |
646 | } | |
647 | ||
648 | static int dc2114x_send(struct udevice *dev, void *packet, int length) | |
649 | { | |
650 | struct dc2114x_priv *priv = dev_get_priv(dev); | |
651 | int ret; | |
652 | ||
653 | ret = dc21x4x_send_common(priv, packet, length); | |
654 | ||
655 | return ret ? 0 : -ETIMEDOUT; | |
656 | } | |
657 | ||
658 | static int dc2114x_recv(struct udevice *dev, int flags, uchar **packetp) | |
659 | { | |
660 | struct dc2114x_priv *priv = dev_get_priv(dev); | |
661 | int ret; | |
662 | ||
663 | ret = dc21x4x_recv_check(priv); | |
664 | ||
665 | if (ret < 0) { | |
666 | /* Update entry information. */ | |
667 | priv->rx_new = (priv->rx_new + 1) % priv->rx_ring_size; | |
668 | ret = 0; | |
669 | } | |
670 | ||
671 | if (!ret) | |
672 | return 0; | |
673 | ||
674 | *packetp = net_rx_packets[priv->rx_new]; | |
675 | ||
676 | return ret - 4; | |
677 | } | |
678 | ||
679 | static int dc2114x_free_pkt(struct udevice *dev, uchar *packet, int length) | |
680 | { | |
681 | struct dc2114x_priv *priv = dev_get_priv(dev); | |
682 | ||
683 | priv->rx_ring[priv->rx_new].status = cpu_to_le32(R_OWN); | |
684 | ||
685 | /* Update entry information. */ | |
686 | priv->rx_new = (priv->rx_new + 1) % priv->rx_ring_size; | |
687 | ||
688 | return 0; | |
689 | } | |
690 | ||
691 | static int dc2114x_read_rom_hwaddr(struct udevice *dev) | |
692 | { | |
693 | struct dc2114x_priv *priv = dev_get_priv(dev); | |
694 | ||
695 | read_hw_addr(priv); | |
696 | ||
697 | return 0; | |
698 | } | |
699 | ||
700 | static int dc2114x_bind(struct udevice *dev) | |
701 | { | |
702 | static int card_number; | |
703 | char name[16]; | |
704 | ||
705 | sprintf(name, "dc2114x#%u", card_number++); | |
706 | ||
707 | return device_set_name(dev, name); | |
708 | } | |
709 | ||
710 | static int dc2114x_probe(struct udevice *dev) | |
711 | { | |
712 | struct eth_pdata *plat = dev_get_platdata(dev); | |
713 | struct dc2114x_priv *priv = dev_get_priv(dev); | |
714 | u16 command, status; | |
715 | u32 iobase; | |
716 | ||
717 | dm_pci_read_config32(dev, PCI_BASE_ADDRESS_1, &iobase); | |
718 | iobase &= ~0xf; | |
719 | ||
720 | debug("dc2114x: DEC 2114x PCI Device @0x%x\n", iobase); | |
721 | ||
722 | priv->devno = dev; | |
723 | priv->enetaddr = plat->enetaddr; | |
724 | priv->iobase = (void __iomem *)dm_pci_mem_to_phys(dev, iobase); | |
725 | ||
726 | command = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | |
727 | dm_pci_write_config16(dev, PCI_COMMAND, command); | |
728 | dm_pci_read_config16(dev, PCI_COMMAND, &status); | |
729 | if ((status & command) != command) { | |
730 | printf("dc2114x: Couldn't enable IO access or Bus Mastering\n"); | |
731 | return -EINVAL; | |
732 | } | |
733 | ||
734 | dm_pci_write_config8(dev, PCI_LATENCY_TIMER, 0x60); | |
735 | ||
736 | return 0; | |
737 | } | |
738 | ||
739 | static const struct eth_ops dc2114x_ops = { | |
740 | .start = dc2114x_start, | |
741 | .send = dc2114x_send, | |
742 | .recv = dc2114x_recv, | |
743 | .stop = dc2114x_stop, | |
744 | .free_pkt = dc2114x_free_pkt, | |
745 | .read_rom_hwaddr = dc2114x_read_rom_hwaddr, | |
746 | }; | |
747 | ||
748 | U_BOOT_DRIVER(eth_dc2114x) = { | |
749 | .name = "eth_dc2114x", | |
750 | .id = UCLASS_ETH, | |
751 | .bind = dc2114x_bind, | |
752 | .probe = dc2114x_probe, | |
753 | .ops = &dc2114x_ops, | |
41575d8e SG |
754 | .priv_auto = sizeof(struct dc2114x_priv), |
755 | .platdata_auto = sizeof(struct eth_pdata), | |
f23a785c MV |
756 | }; |
757 | ||
758 | U_BOOT_PCI_DEVICE(eth_dc2114x, supported); | |
759 | #endif |