]>
Commit | Line | Data |
---|---|---|
3a574cbe WD |
1 | /* |
2 | * ks8695eth.c -- KS8695 ethernet driver | |
3 | * | |
4 | * (C) Copyright 2004-2005, Greg Ungerer <greg.ungerer@opengear.com> | |
5 | * | |
1a459660 | 6 | * SPDX-License-Identifier: GPL-2.0+ |
3a574cbe WD |
7 | */ |
8 | ||
9 | /****************************************************************************/ | |
10 | ||
11 | #include <common.h> | |
12 | #include <malloc.h> | |
13 | #include <net.h> | |
14 | #include <asm/io.h> | |
15 | #include <asm/arch/platform.h> | |
16 | ||
17 | /****************************************************************************/ | |
18 | ||
19 | /* | |
20 | * Hardware register access to the KS8695 LAN ethernet port | |
21 | * (well, it is the 4 port switch really). | |
22 | */ | |
23 | #define ks8695_read(a) *((volatile unsigned long *) (KS8695_IO_BASE + (a))) | |
24 | #define ks8695_write(a,v) *((volatile unsigned long *) (KS8695_IO_BASE + (a))) = (v) | |
25 | ||
26 | /****************************************************************************/ | |
27 | ||
28 | /* | |
29 | * Define the descriptor in-memory data structures. | |
30 | */ | |
31 | struct ks8695_txdesc { | |
32 | uint32_t owner; | |
33 | uint32_t ctrl; | |
34 | uint32_t addr; | |
35 | uint32_t next; | |
36 | }; | |
37 | ||
38 | struct ks8695_rxdesc { | |
39 | uint32_t status; | |
40 | uint32_t ctrl; | |
41 | uint32_t addr; | |
42 | uint32_t next; | |
43 | }; | |
44 | ||
45 | /****************************************************************************/ | |
46 | ||
47 | /* | |
48 | * Allocate local data structures to use for receiving and sending | |
49 | * packets. Just to keep it all nice and simple. | |
50 | */ | |
51 | ||
52 | #define TXDESCS 4 | |
53 | #define RXDESCS 4 | |
54 | #define BUFSIZE 2048 | |
55 | ||
56 | volatile struct ks8695_txdesc ks8695_tx[TXDESCS] __attribute__((aligned(256))); | |
57 | volatile struct ks8695_rxdesc ks8695_rx[RXDESCS] __attribute__((aligned(256))); | |
58 | volatile uint8_t ks8695_bufs[BUFSIZE*(TXDESCS+RXDESCS)] __attribute__((aligned(2048)));; | |
59 | ||
60 | /****************************************************************************/ | |
61 | ||
62 | /* | |
63 | * Ideally we want to use the MAC address stored in flash. | |
64 | * But we do some sanity checks in case they are not present | |
65 | * first. | |
66 | */ | |
67 | unsigned char eth_mac[] = { | |
68 | 0x00, 0x13, 0xc6, 0x00, 0x00, 0x00 | |
69 | }; | |
70 | ||
71 | void ks8695_getmac(void) | |
72 | { | |
73 | unsigned char *fp; | |
74 | int i; | |
75 | ||
76 | /* Check if flash MAC is valid */ | |
77 | fp = (unsigned char *) 0x0201c000; | |
78 | for (i = 0; (i < 6); i++) { | |
79 | if ((fp[i] != 0) && (fp[i] != 0xff)) | |
80 | break; | |
81 | } | |
82 | ||
83 | /* If we found a valid looking MAC address then use it */ | |
84 | if (i < 6) | |
85 | memcpy(ð_mac[0], fp, 6); | |
86 | } | |
87 | ||
88 | /****************************************************************************/ | |
89 | ||
1e8ff714 | 90 | static int ks8695_eth_init(struct eth_device *dev, bd_t *bd) |
3a574cbe WD |
91 | { |
92 | int i; | |
93 | ||
94 | debug ("%s(%d): eth_reset()\n", __FILE__, __LINE__); | |
95 | ||
96 | /* Reset the ethernet engines first */ | |
97 | ks8695_write(KS8695_LAN_DMA_TX, 0x80000000); | |
98 | ks8695_write(KS8695_LAN_DMA_RX, 0x80000000); | |
99 | ||
100 | ks8695_getmac(); | |
101 | ||
102 | /* Set MAC address */ | |
103 | ks8695_write(KS8695_LAN_MAC_LOW, (eth_mac[5] | (eth_mac[4] << 8) | | |
104 | (eth_mac[3] << 16) | (eth_mac[2] << 24))); | |
105 | ks8695_write(KS8695_LAN_MAC_HIGH, (eth_mac[1] | (eth_mac[0] << 8))); | |
106 | ||
107 | /* Turn the 4 port switch on */ | |
108 | i = ks8695_read(KS8695_SWITCH_CTRL0); | |
109 | ks8695_write(KS8695_SWITCH_CTRL0, (i | 0x1)); | |
110 | /* ks8695_write(KS8695_WAN_CONTROL, 0x3f000066); */ | |
111 | ||
112 | /* Initialize descriptor rings */ | |
113 | for (i = 0; (i < TXDESCS); i++) { | |
114 | ks8695_tx[i].owner = 0; | |
115 | ks8695_tx[i].ctrl = 0; | |
116 | ks8695_tx[i].addr = (uint32_t) &ks8695_bufs[i*BUFSIZE]; | |
117 | ks8695_tx[i].next = (uint32_t) &ks8695_tx[i+1]; | |
118 | } | |
119 | ks8695_tx[TXDESCS-1].ctrl = 0x02000000; | |
120 | ks8695_tx[TXDESCS-1].next = (uint32_t) &ks8695_tx[0]; | |
121 | ||
122 | for (i = 0; (i < RXDESCS); i++) { | |
123 | ks8695_rx[i].status = 0x80000000; | |
124 | ks8695_rx[i].ctrl = BUFSIZE - 4; | |
125 | ks8695_rx[i].addr = (uint32_t) &ks8695_bufs[(i+TXDESCS)*BUFSIZE]; | |
126 | ks8695_rx[i].next = (uint32_t) &ks8695_rx[i+1]; | |
127 | } | |
128 | ks8695_rx[RXDESCS-1].ctrl |= 0x00080000; | |
129 | ks8695_rx[RXDESCS-1].next = (uint32_t) &ks8695_rx[0]; | |
130 | ||
131 | /* The KS8695 is pretty slow reseting the ethernets... */ | |
132 | udelay(2000000); | |
133 | ||
134 | /* Enable the ethernet engine */ | |
135 | ks8695_write(KS8695_LAN_TX_LIST, (uint32_t) &ks8695_tx[0]); | |
136 | ks8695_write(KS8695_LAN_RX_LIST, (uint32_t) &ks8695_rx[0]); | |
137 | ks8695_write(KS8695_LAN_DMA_TX, 0x3); | |
138 | ks8695_write(KS8695_LAN_DMA_RX, 0x71); | |
139 | ks8695_write(KS8695_LAN_DMA_RX_START, 0x1); | |
140 | ||
d3f87148 | 141 | printf("KS8695 ETHERNET: %pM\n", eth_mac); |
3a574cbe WD |
142 | return 0; |
143 | } | |
144 | ||
145 | /****************************************************************************/ | |
146 | ||
1e8ff714 | 147 | static void ks8695_eth_halt(struct eth_device *dev) |
3a574cbe WD |
148 | { |
149 | debug ("%s(%d): eth_halt()\n", __FILE__, __LINE__); | |
150 | ||
151 | /* Reset the ethernet engines */ | |
152 | ks8695_write(KS8695_LAN_DMA_TX, 0x80000000); | |
153 | ks8695_write(KS8695_LAN_DMA_RX, 0x80000000); | |
154 | } | |
155 | ||
156 | /****************************************************************************/ | |
157 | ||
1e8ff714 | 158 | static int ks8695_eth_recv(struct eth_device *dev) |
3a574cbe WD |
159 | { |
160 | volatile struct ks8695_rxdesc *dp; | |
161 | int i, len = 0; | |
162 | ||
163 | debug ("%s(%d): eth_rx()\n", __FILE__, __LINE__); | |
164 | ||
165 | for (i = 0; (i < RXDESCS); i++) { | |
166 | dp= &ks8695_rx[i]; | |
167 | if ((dp->status & 0x80000000) == 0) { | |
168 | len = (dp->status & 0x7ff) - 4; | |
169 | NetReceive((void *) dp->addr, len); | |
170 | dp->status = 0x80000000; | |
171 | ks8695_write(KS8695_LAN_DMA_RX_START, 0x1); | |
172 | break; | |
173 | } | |
174 | } | |
175 | ||
176 | return len; | |
177 | } | |
178 | ||
179 | /****************************************************************************/ | |
180 | ||
78a7c17b | 181 | static int ks8695_eth_send(struct eth_device *dev, void *packet, int len) |
3a574cbe WD |
182 | { |
183 | volatile struct ks8695_txdesc *dp; | |
184 | static int next = 0; | |
185 | ||
99724a2d | 186 | debug ("%s(%d): eth_send(packet=%p,len=%d)\n", __FILE__, __LINE__, |
3a574cbe WD |
187 | packet, len); |
188 | ||
189 | dp = &ks8695_tx[next]; | |
611125a2 | 190 | memcpy((void *) dp->addr, (void *) packet, len); |
3a574cbe WD |
191 | |
192 | if (len < 64) { | |
611125a2 | 193 | memset((void *) (dp->addr + len), 0, 64-len); |
3a574cbe WD |
194 | len = 64; |
195 | } | |
196 | ||
197 | dp->ctrl = len | 0xe0000000; | |
198 | dp->owner = 0x80000000; | |
199 | ||
200 | ks8695_write(KS8695_LAN_DMA_TX, 0x3); | |
201 | ks8695_write(KS8695_LAN_DMA_TX_START, 0x1); | |
202 | ||
203 | if (++next >= TXDESCS) | |
204 | next = 0; | |
205 | ||
1e8ff714 GU |
206 | return 0; |
207 | } | |
208 | ||
209 | /****************************************************************************/ | |
210 | ||
211 | int ks8695_eth_initialize(void) | |
212 | { | |
213 | struct eth_device *dev; | |
214 | ||
215 | dev = malloc(sizeof(*dev)); | |
216 | if (dev == NULL) | |
217 | return -1; | |
218 | memset(dev, 0, sizeof(*dev)); | |
219 | ||
220 | dev->iobase = KS8695_IO_BASE + KS8695_LAN_DMA_TX; | |
221 | dev->init = ks8695_eth_init; | |
222 | dev->halt = ks8695_eth_halt; | |
223 | dev->send = ks8695_eth_send; | |
224 | dev->recv = ks8695_eth_recv; | |
225 | strcpy(dev->name, "ks8695eth"); | |
226 | ||
227 | eth_register(dev); | |
228 | return 0; | |
3a574cbe | 229 | } |