]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/boot/powerpc/ps3/ps3net.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / boot / powerpc / ps3 / ps3net.c
1 /*-
2  * Copyright (C) 2010 Nathan Whitehorn
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
22  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28
29 #include <sys/types.h>
30 #include <sys/socket.h>
31   
32 #include <net/if.h>
33 #include <netinet/in.h>
34 #include <netinet/in_systm.h>
35 #include <netinet/if_ether.h>
36 #include <netinet/ip.h>
37
38 #define _KERNEL
39 #include <machine/cpufunc.h>
40
41 #include <stand.h>
42 #include <net.h>
43 #include <netif.h>
44 #include "bootstrap.h"
45 #include "lv1call.h"
46 #include "ps3.h"
47
48 #define GELIC_DESCR_OWNED       0xa0000000
49 #define GELIC_CMDSTAT_NOIPSEC   0x00080000
50 #define GELIC_CMDSTAT_LAST      0x00040000
51 #define GELIC_RXERRORS          0x7def8000
52
53 #define GELIC_POLL_PERIOD       100 /* microseconds */
54
55 static int      ps3net_probe(struct netif *, void *);
56 static int      ps3net_match(struct netif *, void *);
57 static void     ps3net_init(struct iodesc *, void *);
58 static int      ps3net_get(struct iodesc *, void *, size_t, time_t);
59 static int      ps3net_put(struct iodesc *, void *, size_t);
60 static void     ps3net_end(struct netif *);
61
62 struct netif_stats ps3net_stats[1];
63 struct netif_dif ps3net_ifs[] = {{0, 1, ps3net_stats, 0}};
64
65 /* XXX: Get from firmware, not hardcoding */
66 static int busid = 1;
67 static int devid = 0;
68 static int vlan;
69 static uint64_t dma_base;
70
71 struct gelic_dmadesc {
72         uint32_t paddr;
73         uint32_t len;
74         uint32_t next;
75         uint32_t cmd_stat;
76         uint32_t result_size;
77         uint32_t valid_size;
78         uint32_t data_stat;
79         uint32_t rxerror;
80 };
81
82 struct netif_driver ps3net = {
83         "net",
84         ps3net_match,
85         ps3net_probe,
86         ps3net_init,
87         ps3net_get,
88         ps3net_put,
89         ps3net_end,
90         ps3net_ifs, 1
91 };
92
93 static int
94 ps3net_match(struct netif *nif, void *machdep_hint)
95 {
96         return (1);
97 }
98
99 static int
100 ps3net_probe(struct netif *nif, void *machdep_hint)
101 {
102         return (0);
103 }
104
105 static int
106 ps3net_put(struct iodesc *desc, void *pkt, size_t len)
107 {
108         volatile static struct gelic_dmadesc txdesc __aligned(32);
109         volatile static char txbuf[1536] __aligned(128);
110         size_t sendlen;
111         int err;
112
113 #if defined(NETIF_DEBUG)
114         struct ether_header *eh;
115
116         printf("net_put: desc %p, pkt %p, len %d\n", desc, pkt, len);
117         eh = pkt;
118         printf("dst: %s ", ether_sprintf(eh->ether_dhost));
119         printf("src: %s ", ether_sprintf(eh->ether_shost));
120         printf("type: 0x%x\n", eh->ether_type & 0xffff);
121 #endif
122
123         while (txdesc.cmd_stat & GELIC_DESCR_OWNED) {
124                 printf("Stalled XMIT!\n");
125                 delay(10);
126         }
127
128         /*
129          * We must add 4 extra bytes to this packet to store the destination
130          * VLAN. 
131          */
132         memcpy(txbuf, pkt, 12);
133         sendlen = 12;
134         
135         if (vlan >= 0) {
136                 sendlen += 4;
137                 ((uint8_t *)txbuf)[12] = 0x81;
138                 ((uint8_t *)txbuf)[13] = 0x00;
139                 ((uint8_t *)txbuf)[14] = vlan >> 8;
140                 ((uint8_t *)txbuf)[15] = vlan & 0xff;
141         }
142         memcpy((void *)txbuf + sendlen, pkt + 12, len - 12);
143         sendlen += len - 12;
144
145         bzero(&txdesc, sizeof(txdesc));
146         txdesc.paddr = dma_base + (uint32_t)txbuf;
147         txdesc.len = sendlen;
148         txdesc.cmd_stat = GELIC_CMDSTAT_NOIPSEC | GELIC_CMDSTAT_LAST |
149             GELIC_DESCR_OWNED;
150
151         powerpc_sync();
152
153         do {
154                 err = lv1_net_start_tx_dma(busid, devid,
155                     dma_base + (uint32_t)&txdesc, 0);
156                 delay(1);
157                 if (err != 0)
158                         printf("TX Error: %d\n",err);
159         } while (err != 0);
160
161         return (len);
162 }
163
164 static int
165 ps3net_get(struct iodesc *desc, void *pkt, size_t len, time_t timeout)
166 {
167         volatile static struct gelic_dmadesc rxdesc __aligned(32);
168         volatile static char rxbuf[1536] __aligned(128);
169         int err = 0;
170
171         if (len == 0)
172                 goto restartdma;
173
174         timeout *= 1000000; /* convert to microseconds */
175         while (rxdesc.cmd_stat & GELIC_DESCR_OWNED) {
176                 if (timeout < GELIC_POLL_PERIOD) 
177                         return (ETIMEDOUT);
178                 delay(GELIC_POLL_PERIOD);
179                 timeout -= GELIC_POLL_PERIOD;
180         }
181
182         delay(200);
183         if (rxdesc.rxerror & GELIC_RXERRORS) {
184                 err = -1;
185                 goto restartdma;
186         }
187
188         /*
189          * Copy the packet to the receive buffer, leaving out the
190          * 2 byte VLAN header.
191          */
192         len = min(len, rxdesc.valid_size - 2);
193         memcpy(pkt, (u_char *)rxbuf + 2, len);
194         err = len;
195
196 #if defined(NETIF_DEBUG)
197 {
198         struct ether_header *eh;
199
200         printf("net_get: desc %p, pkt %p, len %d\n", desc, pkt, len);
201         eh = pkt;
202         printf("dst: %s ", ether_sprintf(eh->ether_dhost));
203         printf("src: %s ", ether_sprintf(eh->ether_shost));
204         printf("type: 0x%x\n", eh->ether_type & 0xffff);
205 }
206 #endif
207
208 restartdma:
209         lv1_net_stop_rx_dma(busid, devid, 0);
210         powerpc_sync();
211
212         bzero(&rxdesc, sizeof(rxdesc));
213         rxdesc.paddr = dma_base + (uint32_t)rxbuf;
214         rxdesc.len = sizeof(rxbuf);
215         rxdesc.next = 0;
216         rxdesc.cmd_stat = GELIC_DESCR_OWNED;
217         powerpc_sync();
218
219         lv1_net_start_rx_dma(busid, devid, dma_base + (uint32_t)&rxdesc, 0);
220
221         return (err);
222 }
223
224 static void
225 ps3net_init(struct iodesc *desc, void *machdep_hint)
226 {
227         uint64_t mac, val;
228         int i,err;
229
230         err = lv1_open_device(busid, devid, 0);
231
232         lv1_net_stop_tx_dma(busid, devid, 0);
233         lv1_net_stop_rx_dma(busid, devid, 0);
234
235         /*
236          * Wait for link to come up
237          */
238
239         for (i = 0; i < 1000; i++) {
240                 lv1_net_control(busid, devid, GELIC_GET_LINK_STATUS, 2, 0,
241                     0, &val);
242                 if (val & GELIC_LINK_UP)
243                         break;
244                 delay(500);
245         }
246
247         /*
248          * Set up DMA IOMMU entries
249          */
250
251         err = lv1_setup_dma(busid, devid, &dma_base);
252
253         /*
254          * Get MAC address and VLAN IDs
255          */
256
257         lv1_net_control(busid, devid, GELIC_GET_MAC_ADDRESS, 0, 0, 0, &mac);
258         bcopy(&((uint8_t *)&mac)[2], desc->myea, sizeof(desc->myea));
259
260         vlan = -1;
261         err = lv1_net_control(busid, devid, GELIC_GET_VLAN_ID, 2, 0,
262             0, &val);
263         if (err == 0)
264                 vlan = val;
265
266         /*
267          * Start RX DMA engine
268          */
269
270         ps3net_get(NULL, NULL, 0, 0);
271 }
272
273 static void
274 ps3net_end(struct netif *nif)
275 {
276         lv1_close_device(busid, devid);
277 }
278