162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * arch/powerpc/platforms/embedded6xx/usbgecko_udbg.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * udbg serial input/output routines for the USB Gecko adapter. 662306a36Sopenharmony_ci * Copyright (C) 2008-2009 The GameCube Linux Team 762306a36Sopenharmony_ci * Copyright (C) 2008,2009 Albert Herranz 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/of_address.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <mm/mmu_decl.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/io.h> 1562306a36Sopenharmony_ci#include <asm/udbg.h> 1662306a36Sopenharmony_ci#include <asm/fixmap.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "usbgecko_udbg.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define EXI_CLK_32MHZ 5 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define EXI_CSR 0x00 2462306a36Sopenharmony_ci#define EXI_CSR_CLKMASK (0x7<<4) 2562306a36Sopenharmony_ci#define EXI_CSR_CLK_32MHZ (EXI_CLK_32MHZ<<4) 2662306a36Sopenharmony_ci#define EXI_CSR_CSMASK (0x7<<7) 2762306a36Sopenharmony_ci#define EXI_CSR_CS_0 (0x1<<7) /* Chip Select 001 */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define EXI_CR 0x0c 3062306a36Sopenharmony_ci#define EXI_CR_TSTART (1<<0) 3162306a36Sopenharmony_ci#define EXI_CR_WRITE (1<<2) 3262306a36Sopenharmony_ci#define EXI_CR_READ_WRITE (2<<2) 3362306a36Sopenharmony_ci#define EXI_CR_TLEN(len) (((len)-1)<<4) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define EXI_DATA 0x10 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define UG_READ_ATTEMPTS 100 3862306a36Sopenharmony_ci#define UG_WRITE_ATTEMPTS 100 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void __iomem *ug_io_base; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * Performs one input/output transaction between the exi host and the usbgecko. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic u32 ug_io_transaction(u32 in) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci u32 __iomem *csr_reg = ug_io_base + EXI_CSR; 4962306a36Sopenharmony_ci u32 __iomem *data_reg = ug_io_base + EXI_DATA; 5062306a36Sopenharmony_ci u32 __iomem *cr_reg = ug_io_base + EXI_CR; 5162306a36Sopenharmony_ci u32 csr, data, cr; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* select */ 5462306a36Sopenharmony_ci csr = EXI_CSR_CLK_32MHZ | EXI_CSR_CS_0; 5562306a36Sopenharmony_ci out_be32(csr_reg, csr); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* read/write */ 5862306a36Sopenharmony_ci data = in; 5962306a36Sopenharmony_ci out_be32(data_reg, data); 6062306a36Sopenharmony_ci cr = EXI_CR_TLEN(2) | EXI_CR_READ_WRITE | EXI_CR_TSTART; 6162306a36Sopenharmony_ci out_be32(cr_reg, cr); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci while (in_be32(cr_reg) & EXI_CR_TSTART) 6462306a36Sopenharmony_ci barrier(); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* deselect */ 6762306a36Sopenharmony_ci out_be32(csr_reg, 0); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* result */ 7062306a36Sopenharmony_ci data = in_be32(data_reg); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return data; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* 7662306a36Sopenharmony_ci * Returns true if an usbgecko adapter is found. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cistatic int ug_is_adapter_present(void) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci if (!ug_io_base) 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return ug_io_transaction(0x90000000) == 0x04700000; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* 8762306a36Sopenharmony_ci * Returns true if the TX fifo is ready for transmission. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic int ug_is_txfifo_ready(void) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return ug_io_transaction(0xc0000000) & 0x04000000; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Tries to transmit a character. 9662306a36Sopenharmony_ci * If the TX fifo is not ready the result is undefined. 9762306a36Sopenharmony_ci */ 9862306a36Sopenharmony_cistatic void ug_raw_putc(char ch) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci ug_io_transaction(0xb0000000 | (ch << 20)); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* 10462306a36Sopenharmony_ci * Transmits a character. 10562306a36Sopenharmony_ci * It silently fails if the TX fifo is not ready after a number of retries. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_cistatic void ug_putc(char ch) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci int count = UG_WRITE_ATTEMPTS; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (!ug_io_base) 11262306a36Sopenharmony_ci return; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (ch == '\n') 11562306a36Sopenharmony_ci ug_putc('\r'); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci while (!ug_is_txfifo_ready() && count--) 11862306a36Sopenharmony_ci barrier(); 11962306a36Sopenharmony_ci if (count >= 0) 12062306a36Sopenharmony_ci ug_raw_putc(ch); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * Returns true if the RX fifo is ready for transmission. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_cistatic int ug_is_rxfifo_ready(void) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci return ug_io_transaction(0xd0000000) & 0x04000000; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * Tries to receive a character. 13362306a36Sopenharmony_ci * If a character is unavailable the function returns -1. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic int ug_raw_getc(void) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci u32 data = ug_io_transaction(0xa0000000); 13862306a36Sopenharmony_ci if (data & 0x08000000) 13962306a36Sopenharmony_ci return (data >> 16) & 0xff; 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci return -1; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci/* 14562306a36Sopenharmony_ci * Receives a character. 14662306a36Sopenharmony_ci * It fails if the RX fifo is not ready after a number of retries. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_cistatic int ug_getc(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int count = UG_READ_ATTEMPTS; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!ug_io_base) 15362306a36Sopenharmony_ci return -1; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci while (!ug_is_rxfifo_ready() && count--) 15662306a36Sopenharmony_ci barrier(); 15762306a36Sopenharmony_ci return ug_raw_getc(); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci/* 16162306a36Sopenharmony_ci * udbg functions. 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * Transmits a character. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistatic void ug_udbg_putc(char ch) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci ug_putc(ch); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/* 17462306a36Sopenharmony_ci * Receives a character. Waits until a character is available. 17562306a36Sopenharmony_ci */ 17662306a36Sopenharmony_cistatic int ug_udbg_getc(void) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci int ch; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci while ((ch = ug_getc()) == -1) 18162306a36Sopenharmony_ci barrier(); 18262306a36Sopenharmony_ci return ch; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Receives a character. If a character is not available, returns -1. 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_cistatic int ug_udbg_getc_poll(void) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci if (!ug_is_rxfifo_ready()) 19162306a36Sopenharmony_ci return -1; 19262306a36Sopenharmony_ci return ug_getc(); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * Checks if a USB Gecko adapter is inserted in any memory card slot. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_cistatic void __iomem *__init ug_udbg_probe(void __iomem *exi_io_base) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int i; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* look for a usbgecko on memcard slots A and B */ 20362306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 20462306a36Sopenharmony_ci ug_io_base = exi_io_base + 0x14 * i; 20562306a36Sopenharmony_ci if (ug_is_adapter_present()) 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci if (i == 2) 20962306a36Sopenharmony_ci ug_io_base = NULL; 21062306a36Sopenharmony_ci return ug_io_base; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/* 21562306a36Sopenharmony_ci * USB Gecko udbg support initialization. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_civoid __init ug_udbg_init(void) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct device_node *np; 22062306a36Sopenharmony_ci void __iomem *exi_io_base; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (ug_io_base) 22362306a36Sopenharmony_ci udbg_printf("%s: early -> final\n", __func__); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "nintendo,flipper-exi"); 22662306a36Sopenharmony_ci if (!np) { 22762306a36Sopenharmony_ci udbg_printf("%s: EXI node not found\n", __func__); 22862306a36Sopenharmony_ci goto out; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci exi_io_base = of_iomap(np, 0); 23262306a36Sopenharmony_ci if (!exi_io_base) { 23362306a36Sopenharmony_ci udbg_printf("%s: failed to setup EXI io base\n", __func__); 23462306a36Sopenharmony_ci goto done; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (!ug_udbg_probe(exi_io_base)) { 23862306a36Sopenharmony_ci udbg_printf("usbgecko_udbg: not found\n"); 23962306a36Sopenharmony_ci iounmap(exi_io_base); 24062306a36Sopenharmony_ci } else { 24162306a36Sopenharmony_ci udbg_putc = ug_udbg_putc; 24262306a36Sopenharmony_ci udbg_getc = ug_udbg_getc; 24362306a36Sopenharmony_ci udbg_getc_poll = ug_udbg_getc_poll; 24462306a36Sopenharmony_ci udbg_printf("usbgecko_udbg: ready\n"); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cidone: 24862306a36Sopenharmony_ci of_node_put(np); 24962306a36Sopenharmony_ciout: 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci#ifdef CONFIG_PPC_EARLY_DEBUG_USBGECKO 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic phys_addr_t __init ug_early_grab_io_addr(void) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci#if defined(CONFIG_GAMECUBE) 25862306a36Sopenharmony_ci return 0x0c000000; 25962306a36Sopenharmony_ci#elif defined(CONFIG_WII) 26062306a36Sopenharmony_ci return 0x0d000000; 26162306a36Sopenharmony_ci#else 26262306a36Sopenharmony_ci#error Invalid platform for USB Gecko based early debugging. 26362306a36Sopenharmony_ci#endif 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci/* 26762306a36Sopenharmony_ci * USB Gecko early debug support initialization for udbg. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_civoid __init udbg_init_usbgecko(void) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci void __iomem *early_debug_area; 27262306a36Sopenharmony_ci void __iomem *exi_io_base; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * At this point we have a BAT already setup that enables I/O 27662306a36Sopenharmony_ci * to the EXI hardware. 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * The BAT uses a virtual address range reserved at the fixmap. 27962306a36Sopenharmony_ci * This must match the virtual address configured in 28062306a36Sopenharmony_ci * head_32.S:setup_usbgecko_bat(). 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci early_debug_area = (void __iomem *)__fix_to_virt(FIX_EARLY_DEBUG_BASE); 28362306a36Sopenharmony_ci exi_io_base = early_debug_area + 0x00006800; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* try to detect a USB Gecko */ 28662306a36Sopenharmony_ci if (!ug_udbg_probe(exi_io_base)) 28762306a36Sopenharmony_ci return; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* we found a USB Gecko, load udbg hooks */ 29062306a36Sopenharmony_ci udbg_putc = ug_udbg_putc; 29162306a36Sopenharmony_ci udbg_getc = ug_udbg_getc; 29262306a36Sopenharmony_ci udbg_getc_poll = ug_udbg_getc_poll; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * Prepare again the same BAT for MMU_init. 29662306a36Sopenharmony_ci * This allows udbg I/O to continue working after the MMU is 29762306a36Sopenharmony_ci * turned on for real. 29862306a36Sopenharmony_ci * It is safe to continue using the same virtual address as it is 29962306a36Sopenharmony_ci * a reserved fixmap area. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_ci setbat(1, (unsigned long)early_debug_area, 30262306a36Sopenharmony_ci ug_early_grab_io_addr(), 128*1024, PAGE_KERNEL_NCG); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci#endif /* CONFIG_PPC_EARLY_DEBUG_USBGECKO */ 30662306a36Sopenharmony_ci 307