treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 152
[sfrench/cifs-2.6.git] / arch / powerpc / platforms / ps3 / gelic_udbg.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * udbg debug output routine via GELIC UDP broadcasts
4  *
5  * Copyright (C) 2007 Sony Computer Entertainment Inc.
6  * Copyright 2006, 2007 Sony Corporation
7  * Copyright (C) 2010 Hector Martin <hector@marcansoft.com>
8  * Copyright (C) 2011 Andre Heider <a.heider@gmail.com>
9  */
10
11 #include <linux/if_ether.h>
12 #include <linux/etherdevice.h>
13 #include <linux/if_vlan.h>
14 #include <linux/ip.h>
15 #include <linux/udp.h>
16
17 #include <asm/io.h>
18 #include <asm/udbg.h>
19 #include <asm/lv1call.h>
20
21 #define GELIC_BUS_ID 1
22 #define GELIC_DEVICE_ID 0
23 #define GELIC_DEBUG_PORT 18194
24 #define GELIC_MAX_MESSAGE_SIZE 1000
25
26 #define GELIC_LV1_GET_MAC_ADDRESS 1
27 #define GELIC_LV1_GET_VLAN_ID 4
28 #define GELIC_LV1_VLAN_TX_ETHERNET_0 2
29
30 #define GELIC_DESCR_DMA_STAT_MASK 0xf0000000
31 #define GELIC_DESCR_DMA_CARDOWNED 0xa0000000
32
33 #define GELIC_DESCR_TX_DMA_IKE 0x00080000
34 #define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000
35 #define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000
36
37 #define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \
38                                        GELIC_DESCR_TX_DMA_IKE | \
39                                        GELIC_DESCR_TX_DMA_NO_CHKSUM)
40
41 static u64 bus_addr;
42
43 struct gelic_descr {
44         /* as defined by the hardware */
45         __be32 buf_addr;
46         __be32 buf_size;
47         __be32 next_descr_addr;
48         __be32 dmac_cmd_status;
49         __be32 result_size;
50         __be32 valid_size;      /* all zeroes for tx */
51         __be32 data_status;
52         __be32 data_error;      /* all zeroes for tx */
53 } __attribute__((aligned(32)));
54
55 struct debug_block {
56         struct gelic_descr descr;
57         u8 pkt[1520];
58 } __packed;
59
60 static __iomem struct ethhdr *h_eth;
61 static __iomem struct vlan_hdr *h_vlan;
62 static __iomem struct iphdr *h_ip;
63 static __iomem struct udphdr *h_udp;
64
65 static __iomem char *pmsg;
66 static __iomem char *pmsgc;
67
68 static __iomem struct debug_block dbg __attribute__((aligned(32)));
69
70 static int header_size;
71
72 static void map_dma_mem(int bus_id, int dev_id, void *start, size_t len,
73                         u64 *real_bus_addr)
74 {
75         s64 result;
76         u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL;
77         u64 real_end = real_addr + len;
78         u64 map_start = real_addr & ~0xfff;
79         u64 map_end = (real_end + 0xfff) & ~0xfff;
80         u64 bus_addr = 0;
81
82         u64 flags = 0xf800000000000000UL;
83
84         result = lv1_allocate_device_dma_region(bus_id, dev_id,
85                                                 map_end - map_start, 12, 0,
86                                                 &bus_addr);
87         if (result)
88                 lv1_panic(0);
89
90         result = lv1_map_device_dma_region(bus_id, dev_id, map_start,
91                                            bus_addr, map_end - map_start,
92                                            flags);
93         if (result)
94                 lv1_panic(0);
95
96         *real_bus_addr = bus_addr + real_addr - map_start;
97 }
98
99 static int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len)
100 {
101         s64 result;
102         u64 real_bus_addr;
103
104         real_bus_addr = bus_addr & ~0xfff;
105         len += bus_addr - real_bus_addr;
106         len = (len + 0xfff) & ~0xfff;
107
108         result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr,
109                                              len);
110         if (result)
111                 return result;
112
113         return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr);
114 }
115
116 static void gelic_debug_init(void)
117 {
118         s64 result;
119         u64 v2;
120         u64 mac;
121         u64 vlan_id;
122
123         result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0);
124         if (result)
125                 lv1_panic(0);
126
127         map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg),
128                     &bus_addr);
129
130         memset(&dbg, 0, sizeof(dbg));
131
132         dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt);
133
134         wmb();
135
136         result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID,
137                                  GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0,
138                                  &mac, &v2);
139         if (result)
140                 lv1_panic(0);
141
142         mac <<= 16;
143
144         h_eth = (struct ethhdr *)dbg.pkt;
145
146         eth_broadcast_addr(h_eth->h_dest);
147         memcpy(&h_eth->h_source, &mac, ETH_ALEN);
148
149         header_size = sizeof(struct ethhdr);
150
151         result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID,
152                                  GELIC_LV1_GET_VLAN_ID,
153                                  GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0,
154                                  &vlan_id, &v2);
155         if (!result) {
156                 h_eth->h_proto= ETH_P_8021Q;
157
158                 header_size += sizeof(struct vlan_hdr);
159                 h_vlan = (struct vlan_hdr *)(h_eth + 1);
160                 h_vlan->h_vlan_TCI = vlan_id;
161                 h_vlan->h_vlan_encapsulated_proto = ETH_P_IP;
162                 h_ip = (struct iphdr *)(h_vlan + 1);
163         } else {
164                 h_eth->h_proto= 0x0800;
165                 h_ip = (struct iphdr *)(h_eth + 1);
166         }
167
168         header_size += sizeof(struct iphdr);
169         h_ip->version = 4;
170         h_ip->ihl = 5;
171         h_ip->ttl = 10;
172         h_ip->protocol = 0x11;
173         h_ip->saddr = 0x00000000;
174         h_ip->daddr = 0xffffffff;
175
176         header_size += sizeof(struct udphdr);
177         h_udp = (struct udphdr *)(h_ip + 1);
178         h_udp->source = GELIC_DEBUG_PORT;
179         h_udp->dest = GELIC_DEBUG_PORT;
180
181         pmsgc = pmsg = (char *)(h_udp + 1);
182 }
183
184 static void gelic_debug_shutdown(void)
185 {
186         if (bus_addr)
187                 unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID,
188                               bus_addr, sizeof(dbg));
189         lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID);
190 }
191
192 static void gelic_sendbuf(int msgsize)
193 {
194         u16 *p;
195         u32 sum;
196         int i;
197
198         dbg.descr.buf_size = header_size + msgsize;
199         h_ip->tot_len = msgsize + sizeof(struct udphdr) +
200                              sizeof(struct iphdr);
201         h_udp->len = msgsize + sizeof(struct udphdr);
202
203         h_ip->check = 0;
204         sum = 0;
205         p = (u16 *)h_ip;
206         for (i = 0; i < 5; i++)
207                 sum += *p++;
208         h_ip->check = ~(sum + (sum >> 16));
209
210         dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM |
211                                     GELIC_DESCR_TX_DMA_FRAME_TAIL;
212         dbg.descr.result_size = 0;
213         dbg.descr.data_status = 0;
214
215         wmb();
216
217         lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0);
218
219         while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) ==
220                GELIC_DESCR_DMA_CARDOWNED)
221                 cpu_relax();
222 }
223
224 static void ps3gelic_udbg_putc(char ch)
225 {
226         *pmsgc++ = ch;
227         if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) {
228                 gelic_sendbuf(pmsgc-pmsg);
229                 pmsgc = pmsg;
230         }
231 }
232
233 void __init udbg_init_ps3gelic(void)
234 {
235         gelic_debug_init();
236         udbg_putc = ps3gelic_udbg_putc;
237 }
238
239 void udbg_shutdown_ps3gelic(void)
240 {
241         udbg_putc = NULL;
242         gelic_debug_shutdown();
243 }
244 EXPORT_SYMBOL(udbg_shutdown_ps3gelic);