diff options
author | Hector Martin <hector@marcansoft.com> | 2011-08-31 02:32:26 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-19 19:20:05 -0400 |
commit | c26afe9e8591f306d79aab8071f1d34e4f60b700 (patch) | |
tree | ee3e49fef2343c1bf8e752c4a29be9371c143a24 /arch/powerpc/platforms | |
parent | 8feaa43494cee5e938fd5a57b9e9bf1c827e6ccd (diff) |
powerpc/ps3: Add gelic udbg driver
Add a new udbg driver for the PS3 gelic Ehthernet device.
This driver shares only a few stucture and constant definitions with the
gelic Ethernet device driver, so is implemented as a stand-alone driver
with no dependencies on the gelic Ethernet device driver.
Signed-off-by: Hector Martin <hector@marcansoft.com>
Signed-off-by: Andre Heider <a.heider@gmail.com>
Signed-off-by: Geoff Levand <geoff@infradead.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r-- | arch/powerpc/platforms/ps3/Kconfig | 12 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/ps3/gelic_udbg.c | 273 |
3 files changed, 286 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/ps3/Kconfig b/arch/powerpc/platforms/ps3/Kconfig index dfe316b161a9..476d9d9b2405 100644 --- a/arch/powerpc/platforms/ps3/Kconfig +++ b/arch/powerpc/platforms/ps3/Kconfig | |||
@@ -148,4 +148,16 @@ config PS3_LPM | |||
148 | profiling support of the Cell processor with programs like | 148 | profiling support of the Cell processor with programs like |
149 | oprofile and perfmon2, then say Y or M, otherwise say N. | 149 | oprofile and perfmon2, then say Y or M, otherwise say N. |
150 | 150 | ||
151 | config PS3GELIC_UDBG | ||
152 | bool "PS3 udbg output via UDP broadcasts on Ethernet" | ||
153 | depends on PPC_PS3 | ||
154 | help | ||
155 | Enables udbg early debugging output by sending broadcast UDP | ||
156 | via the Ethernet port (UDP port number 18194). | ||
157 | |||
158 | This driver uses a trivial implementation and is independent | ||
159 | from the main network driver. | ||
160 | |||
161 | If in doubt, say N here. | ||
162 | |||
151 | endmenu | 163 | endmenu |
diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index ac1bdf844eca..02b9e636dab7 100644 --- a/arch/powerpc/platforms/ps3/Makefile +++ b/arch/powerpc/platforms/ps3/Makefile | |||
@@ -2,6 +2,7 @@ obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o | |||
2 | obj-y += interrupt.o exports.o os-area.o | 2 | obj-y += interrupt.o exports.o os-area.o |
3 | obj-y += system-bus.o | 3 | obj-y += system-bus.o |
4 | 4 | ||
5 | obj-$(CONFIG_PS3GELIC_UDBG) += gelic_udbg.o | ||
5 | obj-$(CONFIG_SMP) += smp.o | 6 | obj-$(CONFIG_SMP) += smp.o |
6 | obj-$(CONFIG_SPU_BASE) += spu.o | 7 | obj-$(CONFIG_SPU_BASE) += spu.o |
7 | obj-y += device-init.o | 8 | obj-y += device-init.o |
diff --git a/arch/powerpc/platforms/ps3/gelic_udbg.c b/arch/powerpc/platforms/ps3/gelic_udbg.c new file mode 100644 index 000000000000..20b46a19a48f --- /dev/null +++ b/arch/powerpc/platforms/ps3/gelic_udbg.c | |||
@@ -0,0 +1,273 @@ | |||
1 | /* | ||
2 | * udbg debug output routine via GELIC UDP broadcasts | ||
3 | * | ||
4 | * Copyright (C) 2007 Sony Computer Entertainment Inc. | ||
5 | * Copyright 2006, 2007 Sony Corporation | ||
6 | * Copyright (C) 2010 Hector Martin <hector@marcansoft.com> | ||
7 | * Copyright (C) 2011 Andre Heider <a.heider@gmail.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (at your option) any later version. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <asm/io.h> | ||
17 | #include <asm/udbg.h> | ||
18 | #include <asm/lv1call.h> | ||
19 | |||
20 | #define GELIC_BUS_ID 1 | ||
21 | #define GELIC_DEVICE_ID 0 | ||
22 | #define GELIC_DEBUG_PORT 18194 | ||
23 | #define GELIC_MAX_MESSAGE_SIZE 1000 | ||
24 | |||
25 | #define GELIC_LV1_GET_MAC_ADDRESS 1 | ||
26 | #define GELIC_LV1_GET_VLAN_ID 4 | ||
27 | #define GELIC_LV1_VLAN_TX_ETHERNET_0 2 | ||
28 | |||
29 | #define GELIC_DESCR_DMA_STAT_MASK 0xf0000000 | ||
30 | #define GELIC_DESCR_DMA_CARDOWNED 0xa0000000 | ||
31 | |||
32 | #define GELIC_DESCR_TX_DMA_IKE 0x00080000 | ||
33 | #define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000 | ||
34 | #define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000 | ||
35 | |||
36 | #define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \ | ||
37 | GELIC_DESCR_TX_DMA_IKE | \ | ||
38 | GELIC_DESCR_TX_DMA_NO_CHKSUM) | ||
39 | |||
40 | static u64 bus_addr; | ||
41 | |||
42 | struct gelic_descr { | ||
43 | /* as defined by the hardware */ | ||
44 | __be32 buf_addr; | ||
45 | __be32 buf_size; | ||
46 | __be32 next_descr_addr; | ||
47 | __be32 dmac_cmd_status; | ||
48 | __be32 result_size; | ||
49 | __be32 valid_size; /* all zeroes for tx */ | ||
50 | __be32 data_status; | ||
51 | __be32 data_error; /* all zeroes for tx */ | ||
52 | } __attribute__((aligned(32))); | ||
53 | |||
54 | struct debug_block { | ||
55 | struct gelic_descr descr; | ||
56 | u8 pkt[1520]; | ||
57 | } __packed; | ||
58 | |||
59 | struct ethhdr { | ||
60 | u8 dest[6]; | ||
61 | u8 src[6]; | ||
62 | u16 type; | ||
63 | } __packed; | ||
64 | |||
65 | struct vlantag { | ||
66 | u16 vlan; | ||
67 | u16 subtype; | ||
68 | } __packed; | ||
69 | |||
70 | struct iphdr { | ||
71 | u8 ver_len; | ||
72 | u8 dscp_ecn; | ||
73 | u16 total_length; | ||
74 | u16 ident; | ||
75 | u16 frag_off_flags; | ||
76 | u8 ttl; | ||
77 | u8 proto; | ||
78 | u16 checksum; | ||
79 | u32 src; | ||
80 | u32 dest; | ||
81 | } __packed; | ||
82 | |||
83 | struct udphdr { | ||
84 | u16 src; | ||
85 | u16 dest; | ||
86 | u16 len; | ||
87 | u16 checksum; | ||
88 | } __packed; | ||
89 | |||
90 | static __iomem struct ethhdr *h_eth; | ||
91 | static __iomem struct vlantag *h_vlan; | ||
92 | static __iomem struct iphdr *h_ip; | ||
93 | static __iomem struct udphdr *h_udp; | ||
94 | |||
95 | static __iomem char *pmsg; | ||
96 | static __iomem char *pmsgc; | ||
97 | |||
98 | static __iomem struct debug_block dbg __attribute__((aligned(32))); | ||
99 | |||
100 | static int header_size; | ||
101 | |||
102 | static void map_dma_mem(int bus_id, int dev_id, void *start, size_t len, | ||
103 | u64 *real_bus_addr) | ||
104 | { | ||
105 | s64 result; | ||
106 | u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL; | ||
107 | u64 real_end = real_addr + len; | ||
108 | u64 map_start = real_addr & ~0xfff; | ||
109 | u64 map_end = (real_end + 0xfff) & ~0xfff; | ||
110 | u64 bus_addr = 0; | ||
111 | |||
112 | u64 flags = 0xf800000000000000UL; | ||
113 | |||
114 | result = lv1_allocate_device_dma_region(bus_id, dev_id, | ||
115 | map_end - map_start, 12, 0, | ||
116 | &bus_addr); | ||
117 | if (result) | ||
118 | lv1_panic(0); | ||
119 | |||
120 | result = lv1_map_device_dma_region(bus_id, dev_id, map_start, | ||
121 | bus_addr, map_end - map_start, | ||
122 | flags); | ||
123 | if (result) | ||
124 | lv1_panic(0); | ||
125 | |||
126 | *real_bus_addr = bus_addr + real_addr - map_start; | ||
127 | } | ||
128 | |||
129 | static int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len) | ||
130 | { | ||
131 | s64 result; | ||
132 | u64 real_bus_addr; | ||
133 | |||
134 | real_bus_addr = bus_addr & ~0xfff; | ||
135 | len += bus_addr - real_bus_addr; | ||
136 | len = (len + 0xfff) & ~0xfff; | ||
137 | |||
138 | result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr, | ||
139 | len); | ||
140 | if (result) | ||
141 | return result; | ||
142 | |||
143 | return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr); | ||
144 | } | ||
145 | |||
146 | static void gelic_debug_init(void) | ||
147 | { | ||
148 | s64 result; | ||
149 | u64 v2; | ||
150 | u64 mac; | ||
151 | u64 vlan_id; | ||
152 | |||
153 | result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0); | ||
154 | if (result) | ||
155 | lv1_panic(0); | ||
156 | |||
157 | map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg), | ||
158 | &bus_addr); | ||
159 | |||
160 | memset(&dbg, 0, sizeof(dbg)); | ||
161 | |||
162 | dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt); | ||
163 | |||
164 | wmb(); | ||
165 | |||
166 | result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, | ||
167 | GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0, | ||
168 | &mac, &v2); | ||
169 | if (result) | ||
170 | lv1_panic(0); | ||
171 | |||
172 | mac <<= 16; | ||
173 | |||
174 | h_eth = (struct ethhdr *)dbg.pkt; | ||
175 | |||
176 | memset(&h_eth->dest, 0xff, 6); | ||
177 | memcpy(&h_eth->src, &mac, 6); | ||
178 | |||
179 | header_size = sizeof(struct ethhdr); | ||
180 | |||
181 | result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, | ||
182 | GELIC_LV1_GET_VLAN_ID, | ||
183 | GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0, | ||
184 | &vlan_id, &v2); | ||
185 | if (!result) { | ||
186 | h_eth->type = 0x8100; | ||
187 | |||
188 | header_size += sizeof(struct vlantag); | ||
189 | h_vlan = (struct vlantag *)(h_eth + 1); | ||
190 | h_vlan->vlan = vlan_id; | ||
191 | h_vlan->subtype = 0x0800; | ||
192 | h_ip = (struct iphdr *)(h_vlan + 1); | ||
193 | } else { | ||
194 | h_eth->type = 0x0800; | ||
195 | h_ip = (struct iphdr *)(h_eth + 1); | ||
196 | } | ||
197 | |||
198 | header_size += sizeof(struct iphdr); | ||
199 | h_ip->ver_len = 0x45; | ||
200 | h_ip->ttl = 10; | ||
201 | h_ip->proto = 0x11; | ||
202 | h_ip->src = 0x00000000; | ||
203 | h_ip->dest = 0xffffffff; | ||
204 | |||
205 | header_size += sizeof(struct udphdr); | ||
206 | h_udp = (struct udphdr *)(h_ip + 1); | ||
207 | h_udp->src = GELIC_DEBUG_PORT; | ||
208 | h_udp->dest = GELIC_DEBUG_PORT; | ||
209 | |||
210 | pmsgc = pmsg = (char *)(h_udp + 1); | ||
211 | } | ||
212 | |||
213 | static void gelic_debug_shutdown(void) | ||
214 | { | ||
215 | if (bus_addr) | ||
216 | unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, | ||
217 | bus_addr, sizeof(dbg)); | ||
218 | lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID); | ||
219 | } | ||
220 | |||
221 | static void gelic_sendbuf(int msgsize) | ||
222 | { | ||
223 | u16 *p; | ||
224 | u32 sum; | ||
225 | int i; | ||
226 | |||
227 | dbg.descr.buf_size = header_size + msgsize; | ||
228 | h_ip->total_length = msgsize + sizeof(struct udphdr) + | ||
229 | sizeof(struct iphdr); | ||
230 | h_udp->len = msgsize + sizeof(struct udphdr); | ||
231 | |||
232 | h_ip->checksum = 0; | ||
233 | sum = 0; | ||
234 | p = (u16 *)h_ip; | ||
235 | for (i = 0; i < 5; i++) | ||
236 | sum += *p++; | ||
237 | h_ip->checksum = ~(sum + (sum >> 16)); | ||
238 | |||
239 | dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM | | ||
240 | GELIC_DESCR_TX_DMA_FRAME_TAIL; | ||
241 | dbg.descr.result_size = 0; | ||
242 | dbg.descr.data_status = 0; | ||
243 | |||
244 | wmb(); | ||
245 | |||
246 | lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0); | ||
247 | |||
248 | while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) == | ||
249 | GELIC_DESCR_DMA_CARDOWNED) | ||
250 | cpu_relax(); | ||
251 | } | ||
252 | |||
253 | static void ps3gelic_udbg_putc(char ch) | ||
254 | { | ||
255 | *pmsgc++ = ch; | ||
256 | if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) { | ||
257 | gelic_sendbuf(pmsgc-pmsg); | ||
258 | pmsgc = pmsg; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | void __init udbg_init_ps3gelic(void) | ||
263 | { | ||
264 | gelic_debug_init(); | ||
265 | udbg_putc = ps3gelic_udbg_putc; | ||
266 | } | ||
267 | |||
268 | void udbg_shutdown_ps3gelic(void) | ||
269 | { | ||
270 | udbg_putc = NULL; | ||
271 | gelic_debug_shutdown(); | ||
272 | } | ||
273 | EXPORT_SYMBOL(udbg_shutdown_ps3gelic); | ||