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
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