162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * udbg debug output routine via GELIC UDP broadcasts 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2007 Sony Computer Entertainment Inc. 662306a36Sopenharmony_ci * Copyright 2006, 2007 Sony Corporation 762306a36Sopenharmony_ci * Copyright (C) 2010 Hector Martin <hector@marcansoft.com> 862306a36Sopenharmony_ci * Copyright (C) 2011 Andre Heider <a.heider@gmail.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/if_ether.h> 1262306a36Sopenharmony_ci#include <linux/etherdevice.h> 1362306a36Sopenharmony_ci#include <linux/if_vlan.h> 1462306a36Sopenharmony_ci#include <linux/ip.h> 1562306a36Sopenharmony_ci#include <linux/udp.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/io.h> 1862306a36Sopenharmony_ci#include <asm/udbg.h> 1962306a36Sopenharmony_ci#include <asm/lv1call.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define GELIC_BUS_ID 1 2262306a36Sopenharmony_ci#define GELIC_DEVICE_ID 0 2362306a36Sopenharmony_ci#define GELIC_DEBUG_PORT 18194 2462306a36Sopenharmony_ci#define GELIC_MAX_MESSAGE_SIZE 1000 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define GELIC_LV1_GET_MAC_ADDRESS 1 2762306a36Sopenharmony_ci#define GELIC_LV1_GET_VLAN_ID 4 2862306a36Sopenharmony_ci#define GELIC_LV1_VLAN_TX_ETHERNET_0 2 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define GELIC_DESCR_DMA_STAT_MASK 0xf0000000 3162306a36Sopenharmony_ci#define GELIC_DESCR_DMA_CARDOWNED 0xa0000000 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define GELIC_DESCR_TX_DMA_IKE 0x00080000 3462306a36Sopenharmony_ci#define GELIC_DESCR_TX_DMA_NO_CHKSUM 0x00000000 3562306a36Sopenharmony_ci#define GELIC_DESCR_TX_DMA_FRAME_TAIL 0x00040000 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define GELIC_DESCR_DMA_CMD_NO_CHKSUM (GELIC_DESCR_DMA_CARDOWNED | \ 3862306a36Sopenharmony_ci GELIC_DESCR_TX_DMA_IKE | \ 3962306a36Sopenharmony_ci GELIC_DESCR_TX_DMA_NO_CHKSUM) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic u64 bus_addr; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct gelic_descr { 4462306a36Sopenharmony_ci /* as defined by the hardware */ 4562306a36Sopenharmony_ci __be32 buf_addr; 4662306a36Sopenharmony_ci __be32 buf_size; 4762306a36Sopenharmony_ci __be32 next_descr_addr; 4862306a36Sopenharmony_ci __be32 dmac_cmd_status; 4962306a36Sopenharmony_ci __be32 result_size; 5062306a36Sopenharmony_ci __be32 valid_size; /* all zeroes for tx */ 5162306a36Sopenharmony_ci __be32 data_status; 5262306a36Sopenharmony_ci __be32 data_error; /* all zeroes for tx */ 5362306a36Sopenharmony_ci} __attribute__((aligned(32))); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct debug_block { 5662306a36Sopenharmony_ci struct gelic_descr descr; 5762306a36Sopenharmony_ci u8 pkt[1520]; 5862306a36Sopenharmony_ci} __packed; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic __iomem struct ethhdr *h_eth; 6162306a36Sopenharmony_cistatic __iomem struct vlan_hdr *h_vlan; 6262306a36Sopenharmony_cistatic __iomem struct iphdr *h_ip; 6362306a36Sopenharmony_cistatic __iomem struct udphdr *h_udp; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic __iomem char *pmsg; 6662306a36Sopenharmony_cistatic __iomem char *pmsgc; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic __iomem struct debug_block dbg __attribute__((aligned(32))); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int header_size; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void map_dma_mem(int bus_id, int dev_id, void *start, size_t len, 7362306a36Sopenharmony_ci u64 *real_bus_addr) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci s64 result; 7662306a36Sopenharmony_ci u64 real_addr = ((u64)start) & 0x0fffffffffffffffUL; 7762306a36Sopenharmony_ci u64 real_end = real_addr + len; 7862306a36Sopenharmony_ci u64 map_start = real_addr & ~0xfff; 7962306a36Sopenharmony_ci u64 map_end = (real_end + 0xfff) & ~0xfff; 8062306a36Sopenharmony_ci u64 bus_addr = 0; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci u64 flags = 0xf800000000000000UL; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci result = lv1_allocate_device_dma_region(bus_id, dev_id, 8562306a36Sopenharmony_ci map_end - map_start, 12, 0, 8662306a36Sopenharmony_ci &bus_addr); 8762306a36Sopenharmony_ci if (result) 8862306a36Sopenharmony_ci lv1_panic(0); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci result = lv1_map_device_dma_region(bus_id, dev_id, map_start, 9162306a36Sopenharmony_ci bus_addr, map_end - map_start, 9262306a36Sopenharmony_ci flags); 9362306a36Sopenharmony_ci if (result) 9462306a36Sopenharmony_ci lv1_panic(0); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci *real_bus_addr = bus_addr + real_addr - map_start; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int unmap_dma_mem(int bus_id, int dev_id, u64 bus_addr, size_t len) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci s64 result; 10262306a36Sopenharmony_ci u64 real_bus_addr; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci real_bus_addr = bus_addr & ~0xfff; 10562306a36Sopenharmony_ci len += bus_addr - real_bus_addr; 10662306a36Sopenharmony_ci len = (len + 0xfff) & ~0xfff; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci result = lv1_unmap_device_dma_region(bus_id, dev_id, real_bus_addr, 10962306a36Sopenharmony_ci len); 11062306a36Sopenharmony_ci if (result) 11162306a36Sopenharmony_ci return result; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return lv1_free_device_dma_region(bus_id, dev_id, real_bus_addr); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic void __init gelic_debug_init(void) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci s64 result; 11962306a36Sopenharmony_ci u64 v2; 12062306a36Sopenharmony_ci u64 mac; 12162306a36Sopenharmony_ci u64 vlan_id; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci result = lv1_open_device(GELIC_BUS_ID, GELIC_DEVICE_ID, 0); 12462306a36Sopenharmony_ci if (result) 12562306a36Sopenharmony_ci lv1_panic(0); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci map_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, &dbg, sizeof(dbg), 12862306a36Sopenharmony_ci &bus_addr); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci memset(&dbg, 0, sizeof(dbg)); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci dbg.descr.buf_addr = bus_addr + offsetof(struct debug_block, pkt); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci wmb(); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, 13762306a36Sopenharmony_ci GELIC_LV1_GET_MAC_ADDRESS, 0, 0, 0, 13862306a36Sopenharmony_ci &mac, &v2); 13962306a36Sopenharmony_ci if (result) 14062306a36Sopenharmony_ci lv1_panic(0); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mac <<= 16; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci h_eth = (struct ethhdr *)dbg.pkt; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci eth_broadcast_addr(h_eth->h_dest); 14762306a36Sopenharmony_ci memcpy(&h_eth->h_source, &mac, ETH_ALEN); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci header_size = sizeof(struct ethhdr); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci result = lv1_net_control(GELIC_BUS_ID, GELIC_DEVICE_ID, 15262306a36Sopenharmony_ci GELIC_LV1_GET_VLAN_ID, 15362306a36Sopenharmony_ci GELIC_LV1_VLAN_TX_ETHERNET_0, 0, 0, 15462306a36Sopenharmony_ci &vlan_id, &v2); 15562306a36Sopenharmony_ci if (!result) { 15662306a36Sopenharmony_ci h_eth->h_proto= ETH_P_8021Q; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci header_size += sizeof(struct vlan_hdr); 15962306a36Sopenharmony_ci h_vlan = (struct vlan_hdr *)(h_eth + 1); 16062306a36Sopenharmony_ci h_vlan->h_vlan_TCI = vlan_id; 16162306a36Sopenharmony_ci h_vlan->h_vlan_encapsulated_proto = ETH_P_IP; 16262306a36Sopenharmony_ci h_ip = (struct iphdr *)(h_vlan + 1); 16362306a36Sopenharmony_ci } else { 16462306a36Sopenharmony_ci h_eth->h_proto= 0x0800; 16562306a36Sopenharmony_ci h_ip = (struct iphdr *)(h_eth + 1); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci header_size += sizeof(struct iphdr); 16962306a36Sopenharmony_ci h_ip->version = 4; 17062306a36Sopenharmony_ci h_ip->ihl = 5; 17162306a36Sopenharmony_ci h_ip->ttl = 10; 17262306a36Sopenharmony_ci h_ip->protocol = 0x11; 17362306a36Sopenharmony_ci h_ip->saddr = 0x00000000; 17462306a36Sopenharmony_ci h_ip->daddr = 0xffffffff; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci header_size += sizeof(struct udphdr); 17762306a36Sopenharmony_ci h_udp = (struct udphdr *)(h_ip + 1); 17862306a36Sopenharmony_ci h_udp->source = GELIC_DEBUG_PORT; 17962306a36Sopenharmony_ci h_udp->dest = GELIC_DEBUG_PORT; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci pmsgc = pmsg = (char *)(h_udp + 1); 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic void gelic_debug_shutdown(void) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci if (bus_addr) 18762306a36Sopenharmony_ci unmap_dma_mem(GELIC_BUS_ID, GELIC_DEVICE_ID, 18862306a36Sopenharmony_ci bus_addr, sizeof(dbg)); 18962306a36Sopenharmony_ci lv1_close_device(GELIC_BUS_ID, GELIC_DEVICE_ID); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void gelic_sendbuf(int msgsize) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci u16 *p; 19562306a36Sopenharmony_ci u32 sum; 19662306a36Sopenharmony_ci int i; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci dbg.descr.buf_size = header_size + msgsize; 19962306a36Sopenharmony_ci h_ip->tot_len = msgsize + sizeof(struct udphdr) + 20062306a36Sopenharmony_ci sizeof(struct iphdr); 20162306a36Sopenharmony_ci h_udp->len = msgsize + sizeof(struct udphdr); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci h_ip->check = 0; 20462306a36Sopenharmony_ci sum = 0; 20562306a36Sopenharmony_ci p = (u16 *)h_ip; 20662306a36Sopenharmony_ci for (i = 0; i < 5; i++) 20762306a36Sopenharmony_ci sum += *p++; 20862306a36Sopenharmony_ci h_ip->check = ~(sum + (sum >> 16)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci dbg.descr.dmac_cmd_status = GELIC_DESCR_DMA_CMD_NO_CHKSUM | 21162306a36Sopenharmony_ci GELIC_DESCR_TX_DMA_FRAME_TAIL; 21262306a36Sopenharmony_ci dbg.descr.result_size = 0; 21362306a36Sopenharmony_ci dbg.descr.data_status = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci wmb(); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci lv1_net_start_tx_dma(GELIC_BUS_ID, GELIC_DEVICE_ID, bus_addr, 0); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci while ((dbg.descr.dmac_cmd_status & GELIC_DESCR_DMA_STAT_MASK) == 22062306a36Sopenharmony_ci GELIC_DESCR_DMA_CARDOWNED) 22162306a36Sopenharmony_ci cpu_relax(); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void ps3gelic_udbg_putc(char ch) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci *pmsgc++ = ch; 22762306a36Sopenharmony_ci if (ch == '\n' || (pmsgc-pmsg) >= GELIC_MAX_MESSAGE_SIZE) { 22862306a36Sopenharmony_ci gelic_sendbuf(pmsgc-pmsg); 22962306a36Sopenharmony_ci pmsgc = pmsg; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_civoid __init udbg_init_ps3gelic(void) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci gelic_debug_init(); 23662306a36Sopenharmony_ci udbg_putc = ps3gelic_udbg_putc; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_civoid udbg_shutdown_ps3gelic(void) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci udbg_putc = NULL; 24262306a36Sopenharmony_ci gelic_debug_shutdown(); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL(udbg_shutdown_ps3gelic); 245