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