ps3net.c revision 303975
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: releng/11.0/sys/boot/powerpc/ps3/ps3net.c 217044 2011-01-06 04:12:29Z nwhitehorn $"); 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 55static int ps3net_probe(struct netif *, void *); 56static int ps3net_match(struct netif *, void *); 57static void ps3net_init(struct iodesc *, void *); 58static int ps3net_get(struct iodesc *, void *, size_t, time_t); 59static int ps3net_put(struct iodesc *, void *, size_t); 60static void ps3net_end(struct netif *); 61 62struct netif_stats ps3net_stats[1]; 63struct netif_dif ps3net_ifs[] = {{0, 1, ps3net_stats, 0}}; 64 65/* XXX: Get from firmware, not hardcoding */ 66static int busid = 1; 67static int devid = 0; 68static int vlan; 69static uint64_t dma_base; 70 71struct 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 82struct 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 93static int 94ps3net_match(struct netif *nif, void *machdep_hint) 95{ 96 return (1); 97} 98 99static int 100ps3net_probe(struct netif *nif, void *machdep_hint) 101{ 102 return (0); 103} 104 105static int 106ps3net_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 164static int 165ps3net_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 208restartdma: 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 224static void 225ps3net_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 273static void 274ps3net_end(struct netif *nif) 275{ 276 lv1_close_device(busid, devid); 277} 278 279